blob: 41de88cdc053fa4c6f8921cd2ee29f1a911642a4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * inode.c
3 *
4 * Copyright (C) 1995, 1996 by Volker Lendecke
5 * Modified for big endian by J.F. Chadima and David S. Miller
6 * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
7 * Modified 1998 Wolfram Pienkoss for NLS
8 * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
9 *
10 */
11
Joe Perchesb41f8b82014-04-08 16:04:14 -070012#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/module.h>
15
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080016#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <asm/byteorder.h>
18
19#include <linux/time.h>
20#include <linux/kernel.h>
21#include <linux/mm.h>
22#include <linux/string.h>
23#include <linux/stat.h>
24#include <linux/errno.h>
25#include <linux/file.h>
26#include <linux/fcntl.h>
27#include <linux/slab.h>
28#include <linux/vmalloc.h>
29#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/vfs.h>
Miklos Szeredi564cd132008-02-08 04:21:46 -080031#include <linux/mount.h>
32#include <linux/seq_file.h>
Ingo Molnar3f07c012017-02-08 18:51:30 +010033#include <linux/sched/signal.h>
Nick Piggin34286d62011-01-07 17:49:57 +110034#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <net/sock.h>
37
Al Viro32c419d2011-01-12 17:37:47 -050038#include "ncp_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "getopt.h"
40
Miklos Szeredi564cd132008-02-08 04:21:46 -080041#define NCP_DEFAULT_FILE_MODE 0600
42#define NCP_DEFAULT_DIR_MODE 0700
43#define NCP_DEFAULT_TIME_OUT 10
44#define NCP_DEFAULT_RETRY_COUNT 20
45
Al Viro94ee8492010-06-07 00:45:56 -040046static void ncp_evict_inode(struct inode *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static void ncp_put_super(struct super_block *);
David Howells726c3342006-06-23 02:02:58 -070048static int ncp_statfs(struct dentry *, struct kstatfs *);
Al Viro34c80b12011-12-08 21:32:45 -050049static int ncp_show_options(struct seq_file *, struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Christoph Lametere18b8902006-12-06 20:33:20 -080051static struct kmem_cache * ncp_inode_cachep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53static struct inode *ncp_alloc_inode(struct super_block *sb)
54{
55 struct ncp_inode_info *ei;
Christoph Lametere94b1762006-12-06 20:33:17 -080056 ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 if (!ei)
58 return NULL;
59 return &ei->vfs_inode;
60}
61
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110062static void ncp_i_callback(struct rcu_head *head)
63{
64 struct inode *inode = container_of(head, struct inode, i_rcu);
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110065 kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
66}
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068static void ncp_destroy_inode(struct inode *inode)
69{
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110070 call_rcu(&inode->i_rcu, ncp_i_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071}
72
Alexey Dobriyan51cc5062008-07-25 19:45:34 -070073static void init_once(void *foo)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
75 struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
76
Christoph Lametera35afb82007-05-16 22:10:57 -070077 mutex_init(&ei->open_mutex);
78 inode_init_once(&ei->vfs_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079}
Paul Mundt20c2df82007-07-20 10:11:58 +090080
Linus Torvalds1da177e2005-04-16 15:20:36 -070081static int init_inodecache(void)
82{
83 ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
84 sizeof(struct ncp_inode_info),
Paul Jacksonfffb60f2006-03-24 03:16:06 -080085 0, (SLAB_RECLAIM_ACCOUNT|
Vladimir Davydov5d097052016-01-14 15:18:21 -080086 SLAB_MEM_SPREAD|SLAB_ACCOUNT),
Paul Mundt20c2df82007-07-20 10:11:58 +090087 init_once);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 if (ncp_inode_cachep == NULL)
89 return -ENOMEM;
90 return 0;
91}
92
93static void destroy_inodecache(void)
94{
Kirill A. Shutemov8c0a8532012-09-26 11:33:07 +100095 /*
96 * Make sure all delayed rcu free inodes are flushed before we
97 * destroy cache.
98 */
99 rcu_barrier();
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -0700100 kmem_cache_destroy(ncp_inode_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
103static int ncp_remount(struct super_block *sb, int *flags, char* data)
104{
Theodore Ts'o02b99842014-03-13 10:14:33 -0400105 sync_filesystem(sb);
Linus Torvalds1751e8a2017-11-27 13:05:09 -0800106 *flags |= SB_NODIRATIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 return 0;
108}
109
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800110static const struct super_operations ncp_sops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 .alloc_inode = ncp_alloc_inode,
113 .destroy_inode = ncp_destroy_inode,
114 .drop_inode = generic_delete_inode,
Al Viro94ee8492010-06-07 00:45:56 -0400115 .evict_inode = ncp_evict_inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 .put_super = ncp_put_super,
117 .statfs = ncp_statfs,
118 .remount_fs = ncp_remount,
Miklos Szeredi564cd132008-02-08 04:21:46 -0800119 .show_options = ncp_show_options,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120};
121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122/*
123 * Fill in the ncpfs-specific information in the inode.
124 */
125static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo)
126{
127 NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
128 NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
129 NCP_FINFO(inode)->volNumber = nwinfo->volume;
130}
131
132void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
133{
134 ncp_update_dirent(inode, nwinfo);
135 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
136 NCP_FINFO(inode)->access = nwinfo->access;
137 memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
138 sizeof(nwinfo->file_handle));
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700139 ncp_dbg(1, "updated %s, volnum=%d, dirent=%u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
141 NCP_FINFO(inode)->dirEntNum);
142}
143
144static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi)
145{
146 /* NFS namespace mode overrides others if it's set. */
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700147 ncp_dbg(1, "(%s) nfs.mode=0%o\n", nwi->entryName, nwi->nfs.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 if (nwi->nfs.mode) {
149 /* XXX Security? */
150 inode->i_mode = nwi->nfs.mode;
151 }
152
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200153 inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate);
156 inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate);
157 inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate);
158 inode->i_atime.tv_nsec = 0;
159 inode->i_mtime.tv_nsec = 0;
160 inode->i_ctime.tv_nsec = 0;
161}
162
163static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo)
164{
165 struct nw_info_struct *nwi = &nwinfo->i;
166 struct ncp_server *server = NCP_SERVER(inode);
167
168 if (nwi->attributes & aDIR) {
169 inode->i_mode = server->m.dir_mode;
170 /* for directories dataStreamSize seems to be some
171 Object ID ??? */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200172 i_size_write(inode, NCP_BLOCK_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 } else {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200174 u32 size;
175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 inode->i_mode = server->m.file_mode;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200177 size = le32_to_cpu(nwi->dataStreamSize);
178 i_size_write(inode, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179#ifdef CONFIG_NCPFS_EXTRAS
180 if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
181 && (nwi->attributes & aSHARED)) {
182 switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
183 case aHIDDEN:
184 if (server->m.flags & NCP_MOUNT_SYMLINKS) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200185 if (/* (size >= NCP_MIN_SYMLINK_SIZE)
186 && */ (size <= NCP_MAX_SYMLINK_SIZE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
188 NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK;
189 break;
190 }
191 }
192 /* FALLTHROUGH */
193 case 0:
194 if (server->m.flags & NCP_MOUNT_EXTRAS)
195 inode->i_mode |= S_IRUGO;
196 break;
197 case aSYSTEM:
198 if (server->m.flags & NCP_MOUNT_EXTRAS)
199 inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO;
200 break;
201 /* case aSYSTEM|aHIDDEN: */
202 default:
203 /* reserved combination */
204 break;
205 }
206 }
207#endif
208 }
209 if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO;
210}
211
212void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
213{
214 NCP_FINFO(inode)->flags = 0;
215 if (!atomic_read(&NCP_FINFO(inode)->opened)) {
216 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
217 ncp_update_attrs(inode, nwinfo);
218 }
219
220 ncp_update_dates(inode, &nwinfo->i);
221 ncp_update_dirent(inode, nwinfo);
222}
223
224/*
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200225 * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 */
227static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
228{
229 struct ncp_server *server = NCP_SERVER(inode);
230
231 NCP_FINFO(inode)->flags = 0;
232
233 ncp_update_attrs(inode, nwinfo);
234
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700235 ncp_dbg(2, "inode->i_mode = %u\n", inode->i_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
Miklos Szeredibfe86842011-10-28 14:13:29 +0200237 set_nlink(inode, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 inode->i_uid = server->m.uid;
239 inode->i_gid = server->m.gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 ncp_update_dates(inode, &nwinfo->i);
242 ncp_update_inode(inode, nwinfo);
243}
244
245#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -0800246static const struct inode_operations ncp_symlink_inode_operations = {
Al Viro6b255392015-11-17 10:20:54 -0500247 .get_link = page_get_link,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 .setattr = ncp_notify_change,
249};
250#endif
251
252/*
253 * Get a new inode.
254 */
255struct inode *
256ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
257{
258 struct inode *inode;
259
260 if (info == NULL) {
Joe Perchesb41f8b82014-04-08 16:04:14 -0700261 pr_err("%s: info is NULL\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return NULL;
263 }
264
265 inode = new_inode(sb);
266 if (inode) {
267 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
268
269 inode->i_ino = info->ino;
270 ncp_set_attr(inode, info);
271 if (S_ISREG(inode->i_mode)) {
272 inode->i_op = &ncp_file_inode_operations;
273 inode->i_fop = &ncp_file_operations;
274 } else if (S_ISDIR(inode->i_mode)) {
275 inode->i_op = &ncp_dir_inode_operations;
276 inode->i_fop = &ncp_dir_operations;
277#ifdef CONFIG_NCPFS_NFS_NS
278 } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
279 init_special_inode(inode, inode->i_mode,
280 new_decode_dev(info->i.nfs.rdev));
281#endif
282#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
283 } else if (S_ISLNK(inode->i_mode)) {
284 inode->i_op = &ncp_symlink_inode_operations;
Al Viro21fc61c2015-11-17 01:07:57 -0500285 inode_nohighmem(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 inode->i_data.a_ops = &ncp_symlink_aops;
287#endif
288 } else {
289 make_bad_inode(inode);
290 }
291 insert_inode_hash(inode);
292 } else
Joe Perchesb41f8b82014-04-08 16:04:14 -0700293 pr_err("%s: iget failed!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 return inode;
295}
296
297static void
Al Viro94ee8492010-06-07 00:45:56 -0400298ncp_evict_inode(struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
Johannes Weiner91b0abe2014-04-03 14:47:49 -0700300 truncate_inode_pages_final(&inode->i_data);
Jan Karadbd57682012-05-03 14:48:02 +0200301 clear_inode(inode);
Mark Fashehfef26652005-09-09 13:01:31 -0700302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 if (S_ISDIR(inode->i_mode)) {
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700304 ncp_dbg(2, "put directory %ld\n", inode->i_ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 }
306
307 if (ncp_make_closed(inode) != 0) {
308 /* We can't do anything but complain. */
Joe Perchesb41f8b82014-04-08 16:04:14 -0700309 pr_err("%s: could not close\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
313static void ncp_stop_tasks(struct ncp_server *server) {
314 struct sock* sk = server->ncp_sock->sk;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200315
316 lock_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 sk->sk_error_report = server->error_report;
318 sk->sk_data_ready = server->data_ready;
319 sk->sk_write_space = server->write_space;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200320 release_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 del_timer_sync(&server->timeout_tm);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100322
Tejun Heo43829732012-08-20 14:51:24 -0700323 flush_work(&server->rcv.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100324 if (sk->sk_socket->type == SOCK_STREAM)
Tejun Heo43829732012-08-20 14:51:24 -0700325 flush_work(&server->tx.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100326 else
Tejun Heo43829732012-08-20 14:51:24 -0700327 flush_work(&server->timeout_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Al Viro34c80b12011-12-08 21:32:45 -0500330static int ncp_show_options(struct seq_file *seq, struct dentry *root)
Miklos Szeredi564cd132008-02-08 04:21:46 -0800331{
Al Viro34c80b12011-12-08 21:32:45 -0500332 struct ncp_server *server = NCP_SBP(root->d_sb);
Miklos Szeredi564cd132008-02-08 04:21:46 -0800333 unsigned int tmp;
334
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800335 if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
336 seq_printf(seq, ",uid=%u",
337 from_kuid_munged(&init_user_ns, server->m.uid));
338 if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
339 seq_printf(seq, ",gid=%u",
340 from_kgid_munged(&init_user_ns, server->m.gid));
341 if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
342 seq_printf(seq, ",owner=%u",
343 from_kuid_munged(&init_user_ns, server->m.mounted_uid));
Miklos Szeredi564cd132008-02-08 04:21:46 -0800344 tmp = server->m.file_mode & S_IALLUGO;
345 if (tmp != NCP_DEFAULT_FILE_MODE)
346 seq_printf(seq, ",mode=0%o", tmp);
347 tmp = server->m.dir_mode & S_IALLUGO;
348 if (tmp != NCP_DEFAULT_DIR_MODE)
349 seq_printf(seq, ",dirmode=0%o", tmp);
350 if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
351 tmp = server->m.time_out * 100 / HZ;
352 seq_printf(seq, ",timeout=%u", tmp);
353 }
354 if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
355 seq_printf(seq, ",retry=%u", server->m.retry_count);
356 if (server->m.flags != 0)
357 seq_printf(seq, ",flags=%lu", server->m.flags);
358 if (server->m.wdog_pid != NULL)
359 seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
360
361 return 0;
362}
363
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364static const struct ncp_option ncp_opts[] = {
365 { "uid", OPT_INT, 'u' },
366 { "gid", OPT_INT, 'g' },
367 { "owner", OPT_INT, 'o' },
368 { "mode", OPT_INT, 'm' },
369 { "dirmode", OPT_INT, 'd' },
370 { "timeout", OPT_INT, 't' },
371 { "retry", OPT_INT, 'r' },
372 { "flags", OPT_INT, 'f' },
373 { "wdogpid", OPT_INT, 'w' },
374 { "ncpfd", OPT_INT, 'n' },
375 { "infofd", OPT_INT, 'i' }, /* v5 */
376 { "version", OPT_INT, 'v' },
377 { NULL, 0, 0 } };
378
379static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
380 int optval;
381 char *optarg;
382 unsigned long optint;
383 int version = 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800384 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
386 data->flags = 0;
387 data->int_flags = 0;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800388 data->mounted_uid = GLOBAL_ROOT_UID;
Eric W. Biederman21542272006-12-13 00:35:11 -0800389 data->wdog_pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 data->ncp_fd = ~0;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800391 data->time_out = NCP_DEFAULT_TIME_OUT;
392 data->retry_count = NCP_DEFAULT_RETRY_COUNT;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800393 data->uid = GLOBAL_ROOT_UID;
394 data->gid = GLOBAL_ROOT_GID;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800395 data->file_mode = NCP_DEFAULT_FILE_MODE;
396 data->dir_mode = NCP_DEFAULT_DIR_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 data->info_fd = -1;
398 data->mounted_vol[0] = 0;
399
400 while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
Eric W. Biederman1de24122006-12-13 00:35:13 -0800401 ret = optval;
402 if (ret < 0)
403 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 switch (optval) {
405 case 'u':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800406 data->uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800407 if (!uid_valid(data->uid)) {
408 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800409 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800410 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 break;
412 case 'g':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800413 data->gid = make_kgid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800414 if (!gid_valid(data->gid)) {
415 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800416 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 break;
419 case 'o':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800420 data->mounted_uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800421 if (!uid_valid(data->mounted_uid)) {
422 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800423 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800424 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 break;
426 case 'm':
427 data->file_mode = optint;
428 break;
429 case 'd':
430 data->dir_mode = optint;
431 break;
432 case 't':
433 data->time_out = optint;
434 break;
435 case 'r':
436 data->retry_count = optint;
437 break;
438 case 'f':
439 data->flags = optint;
440 break;
441 case 'w':
Eric W. Biederman21542272006-12-13 00:35:11 -0800442 data->wdog_pid = find_get_pid(optint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 break;
444 case 'n':
445 data->ncp_fd = optint;
446 break;
447 case 'i':
448 data->info_fd = optint;
449 break;
450 case 'v':
Eric W. Biederman1de24122006-12-13 00:35:13 -0800451 ret = -ECHRNG;
452 if (optint < NCP_MOUNT_VERSION_V4)
453 goto err;
454 if (optint > NCP_MOUNT_VERSION_V5)
455 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 version = optint;
457 break;
458
459 }
460 }
461 return 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800462err:
463 put_pid(data->wdog_pid);
464 data->wdog_pid = NULL;
465 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466}
467
468static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
469{
470 struct ncp_mount_data_kernel data;
471 struct ncp_server *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 struct inode *root_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 struct socket *sock;
474 int error;
475 int default_bufsize;
476#ifdef CONFIG_NCPFS_PACKET_SIGNING
477 int options;
478#endif
479 struct ncp_entry_info finfo;
480
Andrew Morton2a5cac12011-05-24 17:13:42 -0700481 memset(&data, 0, sizeof(data));
Panagiotis Issarisf8314dc2006-09-27 01:49:37 -0700482 server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 if (!server)
484 return -ENOMEM;
485 sb->s_fs_info = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 error = -EFAULT;
488 if (raw_data == NULL)
489 goto out;
490 switch (*(int*)raw_data) {
491 case NCP_MOUNT_VERSION:
492 {
493 struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
494
495 data.flags = md->flags;
496 data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800497 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800498 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 data.ncp_fd = md->ncp_fd;
500 data.time_out = md->time_out;
501 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800502 data.uid = make_kuid(current_user_ns(), md->uid);
503 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 data.file_mode = md->file_mode;
505 data.dir_mode = md->dir_mode;
506 data.info_fd = -1;
507 memcpy(data.mounted_vol, md->mounted_vol,
508 NCP_VOLNAME_LEN+1);
509 }
510 break;
511 case NCP_MOUNT_VERSION_V4:
512 {
513 struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
514
515 data.flags = md->flags;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800516 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800517 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 data.ncp_fd = md->ncp_fd;
519 data.time_out = md->time_out;
520 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800521 data.uid = make_kuid(current_user_ns(), md->uid);
522 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 data.file_mode = md->file_mode;
524 data.dir_mode = md->dir_mode;
525 data.info_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
527 break;
528 default:
529 error = -ECHRNG;
530 if (memcmp(raw_data, "vers", 4) == 0) {
531 error = ncp_parse_options(&data, raw_data);
532 }
533 if (error)
534 goto out;
535 break;
536 }
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800537 error = -EINVAL;
538 if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
539 !gid_valid(data.gid))
540 goto out;
Al Viro44ba84062014-03-06 17:41:01 -0500541 sock = sockfd_lookup(data.ncp_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 if (!sock)
Al Viro44ba84062014-03-06 17:41:01 -0500543 goto out;
544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 if (sock->type == SOCK_STREAM)
546 default_bufsize = 0xF000;
547 else
548 default_bufsize = 1024;
549
Linus Torvalds1751e8a2017-11-27 13:05:09 -0800550 sb->s_flags |= SB_NODIRATIME; /* probably even noatime */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 sb->s_maxbytes = 0xFFFFFFFFU;
552 sb->s_blocksize = 1024; /* Eh... Is this correct? */
553 sb->s_blocksize_bits = 10;
554 sb->s_magic = NCP_SUPER_MAGIC;
555 sb->s_op = &ncp_sops;
Al Viro0378c402011-01-12 17:25:03 -0500556 sb->s_d_op = &ncp_dentry_operations;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 server = NCP_SBP(sb);
559 memset(server, 0, sizeof(*server));
560
Jan Karaa0349ec2017-04-12 12:24:44 +0200561 error = super_setup_bdi(sb);
Jens Axboef1970c72010-04-22 12:31:11 +0200562 if (error)
Djalal Harouni759c3612011-12-13 02:47:29 +0100563 goto out_fput;
Jens Axboef1970c72010-04-22 12:31:11 +0200564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 server->ncp_sock = sock;
566
567 if (data.info_fd != -1) {
Al Viro44ba84062014-03-06 17:41:01 -0500568 struct socket *info_sock = sockfd_lookup(data.info_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 if (!info_sock)
Jan Karaa0349ec2017-04-12 12:24:44 +0200570 goto out_fput;
Al Viro44ba84062014-03-06 17:41:01 -0500571 server->info_sock = info_sock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 error = -EBADFD;
573 if (info_sock->type != SOCK_STREAM)
574 goto out_fput2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576
577/* server->lock = 0; */
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800578 mutex_init(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 server->packet = NULL;
580/* server->buffer_size = 0; */
581/* server->conn_status = 0; */
582/* server->root_dentry = NULL; */
583/* server->root_setuped = 0; */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200584 mutex_init(&server->root_setup_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585#ifdef CONFIG_NCPFS_PACKET_SIGNING
586/* server->sign_wanted = 0; */
587/* server->sign_active = 0; */
588#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200589 init_rwsem(&server->auth_rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 server->auth.auth_type = NCP_AUTH_NONE;
591/* server->auth.object_name_len = 0; */
592/* server->auth.object_name = NULL; */
593/* server->auth.object_type = 0; */
594/* server->priv.len = 0; */
595/* server->priv.data = NULL; */
596
597 server->m = data;
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300598 /* Although anything producing this is buggy, it happens
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 now because of PATH_MAX changes.. */
600 if (server->m.time_out < 1) {
601 server->m.time_out = 10;
Joe Perchesb41f8b82014-04-08 16:04:14 -0700602 pr_info("You need to recompile your ncpfs utils..\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 }
604 server->m.time_out = server->m.time_out * HZ / 100;
605 server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
606 server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
607
608#ifdef CONFIG_NCPFS_NLS
609 /* load the default NLS charsets */
610 server->nls_vol = load_nls_default();
611 server->nls_io = load_nls_default();
612#endif /* CONFIG_NCPFS_NLS */
613
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200614 atomic_set(&server->dentry_ttl, 0); /* no caching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 INIT_LIST_HEAD(&server->tx.requests);
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800617 mutex_init(&server->rcv.creq_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 server->tx.creq = NULL;
619 server->rcv.creq = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
Kees Cook9b5dfbd2017-08-29 21:00:21 -0700621 timer_setup(&server->timeout_tm, ncpdgram_timeout_call, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622#undef NCP_PACKET_SIZE
623#define NCP_PACKET_SIZE 131072
624 error = -ENOMEM;
625 server->packet_size = NCP_PACKET_SIZE;
626 server->packet = vmalloc(NCP_PACKET_SIZE);
627 if (server->packet == NULL)
628 goto out_nls;
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100629 server->txbuf = vmalloc(NCP_PACKET_SIZE);
630 if (server->txbuf == NULL)
631 goto out_packet;
632 server->rxbuf = vmalloc(NCP_PACKET_SIZE);
633 if (server->rxbuf == NULL)
634 goto out_txbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200636 lock_sock(sock->sk);
637 server->data_ready = sock->sk->sk_data_ready;
638 server->write_space = sock->sk->sk_write_space;
639 server->error_report = sock->sk->sk_error_report;
640 sock->sk->sk_user_data = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 sock->sk->sk_data_ready = ncp_tcp_data_ready;
642 sock->sk->sk_error_report = ncp_tcp_error_report;
643 if (sock->type == SOCK_STREAM) {
644 server->rcv.ptr = (unsigned char*)&server->rcv.buf;
645 server->rcv.len = 10;
646 server->rcv.state = 0;
David Howellsc4028952006-11-22 14:57:56 +0000647 INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
648 INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 sock->sk->sk_write_space = ncp_tcp_write_space;
650 } else {
David Howellsc4028952006-11-22 14:57:56 +0000651 INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
652 INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200654 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
656 ncp_lock_server(server);
657 error = ncp_connect(server);
658 ncp_unlock_server(server);
659 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100660 goto out_rxbuf;
Joe Perches15a03ac2014-04-08 16:04:18 -0700661 ncp_dbg(1, "NCP_SBP(sb) = %p\n", NCP_SBP(sb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662
663 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
664#ifdef CONFIG_NCPFS_PACKET_SIGNING
665 if (ncp_negotiate_size_and_options(server, default_bufsize,
666 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
667 {
668 if (options != NCP_DEFAULT_OPTIONS)
669 {
670 if (ncp_negotiate_size_and_options(server,
671 default_bufsize,
672 options & 2,
673 &(server->buffer_size), &options) != 0)
674
675 {
676 goto out_disconnect;
677 }
678 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200679 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 if (options & 2)
681 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200682 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 }
684 else
685#endif /* CONFIG_NCPFS_PACKET_SIGNING */
686 if (ncp_negotiate_buffersize(server, default_bufsize,
687 &(server->buffer_size)) != 0)
688 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700689 ncp_dbg(1, "bufsize = %d\n", server->buffer_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691 memset(&finfo, 0, sizeof(finfo));
692 finfo.i.attributes = aDIR;
693 finfo.i.dataStreamSize = 0; /* ignored */
694 finfo.i.dirEntNum = 0;
695 finfo.i.DosDirNum = 0;
696#ifdef CONFIG_NCPFS_SMALLDOS
697 finfo.i.NSCreator = NW_NS_DOS;
698#endif
699 finfo.volume = NCP_NUMBER_OF_VOLUMES;
700 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
701 finfo.i.creationTime = finfo.i.modifyTime
702 = cpu_to_le16(0x0000);
703 finfo.i.creationDate = finfo.i.modifyDate
704 = finfo.i.lastAccessDate
705 = cpu_to_le16(0x0C21);
706 finfo.i.nameLen = 0;
707 finfo.i.entryName[0] = '\0';
708
709 finfo.opened = 0;
710 finfo.ino = 2; /* tradition */
711
712 server->name_space[finfo.volume] = NW_NS_DOS;
713
714 error = -ENOMEM;
715 root_inode = ncp_iget(sb, &finfo);
716 if (!root_inode)
717 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700718 ncp_dbg(1, "root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500719 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500721 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 return 0;
723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724out_disconnect:
725 ncp_lock_server(server);
726 ncp_disconnect(server);
727 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100728out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100730 vfree(server->rxbuf);
731out_txbuf:
732 vfree(server->txbuf);
733out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 vfree(server->packet);
735out_nls:
736#ifdef CONFIG_NCPFS_NLS
737 unload_nls(server->nls_io);
738 unload_nls(server->nls_vol);
739#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200740 mutex_destroy(&server->rcv.creq_mutex);
741 mutex_destroy(&server->root_setup_lock);
742 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743out_fput2:
Al Viro44ba84062014-03-06 17:41:01 -0500744 if (server->info_sock)
745 sockfd_put(server->info_sock);
Djalal Harouni759c3612011-12-13 02:47:29 +0100746out_fput:
Al Viro44ba84062014-03-06 17:41:01 -0500747 sockfd_put(sock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800749 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 sb->s_fs_info = NULL;
751 kfree(server);
752 return error;
753}
754
Al Viro1dcddd42013-10-03 13:22:44 -0400755static void delayed_free(struct rcu_head *p)
756{
757 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
758#ifdef CONFIG_NCPFS_NLS
759 /* unload the NLS charsets */
760 unload_nls(server->nls_vol);
761 unload_nls(server->nls_io);
762#endif /* CONFIG_NCPFS_NLS */
763 kfree(server);
764}
765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766static void ncp_put_super(struct super_block *sb)
767{
768 struct ncp_server *server = NCP_SBP(sb);
769
770 ncp_lock_server(server);
771 ncp_disconnect(server);
772 ncp_unlock_server(server);
773
774 ncp_stop_tasks(server);
775
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200776 mutex_destroy(&server->rcv.creq_mutex);
777 mutex_destroy(&server->root_setup_lock);
778 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Al Viro44ba84062014-03-06 17:41:01 -0500780 if (server->info_sock)
781 sockfd_put(server->info_sock);
782 sockfd_put(server->ncp_sock);
Eric W. Biederman21542272006-12-13 00:35:11 -0800783 kill_pid(server->m.wdog_pid, SIGTERM, 1);
784 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Pekka Enberg44db77f2006-01-14 13:21:12 -0800786 kfree(server->priv.data);
787 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100788 vfree(server->rxbuf);
789 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400791 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
David Howells726c3342006-06-23 02:02:58 -0700794static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
796 struct dentry* d;
797 struct inode* i;
798 struct ncp_inode_info* ni;
799 struct ncp_server* s;
800 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700801 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 int err;
803 __u8 dh;
804
805 d = sb->s_root;
806 if (!d) {
807 goto dflt;
808 }
David Howells2b0143b2015-03-17 22:25:59 +0000809 i = d_inode(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 if (!i) {
811 goto dflt;
812 }
813 ni = NCP_FINFO(i);
814 if (!ni) {
815 goto dflt;
816 }
817 s = NCP_SBP(sb);
818 if (!s) {
819 goto dflt;
820 }
821 if (!s->m.mounted_vol[0]) {
822 goto dflt;
823 }
824
825 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
826 if (err) {
827 goto dflt;
828 }
829 err = ncp_get_directory_info(s, dh, &vi);
830 ncp_dirhandle_free(s, dh);
831 if (err) {
832 goto dflt;
833 }
834 buf->f_type = NCP_SUPER_MAGIC;
835 buf->f_bsize = vi.sectors_per_block * 512;
836 buf->f_blocks = vi.total_blocks;
837 buf->f_bfree = vi.free_blocks;
838 buf->f_bavail = vi.free_blocks;
839 buf->f_files = vi.total_dir_entries;
840 buf->f_ffree = vi.available_dir_entries;
841 buf->f_namelen = 12;
842 return 0;
843
844 /* We cannot say how much disk space is left on a mounted
845 NetWare Server, because free space is distributed over
846 volumes, and the current user might have disk quotas. So
847 free space is not that simple to determine. Our decision
848 here is to err conservatively. */
849
850dflt:;
851 buf->f_type = NCP_SUPER_MAGIC;
852 buf->f_bsize = NCP_BLOCK_SIZE;
853 buf->f_blocks = 0;
854 buf->f_bfree = 0;
855 buf->f_bavail = 0;
856 buf->f_namelen = 12;
857 return 0;
858}
859
860int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
861{
David Howells2b0143b2015-03-17 22:25:59 +0000862 struct inode *inode = d_inode(dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 int result = 0;
864 __le32 info_mask;
865 struct nw_modify_dos_info info;
866 struct ncp_server *server;
867
868 result = -EIO;
869
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200871 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 goto out;
873
Al Viro338b2f52013-06-15 05:53:23 +0400874 result = -EPERM;
David Howells2b0143b2015-03-17 22:25:59 +0000875 if (IS_DEADDIR(d_inode(dentry)))
Al Viro338b2f52013-06-15 05:53:23 +0400876 goto out;
877
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 /* ageing the dentry to force validation */
879 ncp_age_dentry(server, dentry);
880
Jan Kara31051c82016-05-26 16:55:18 +0200881 result = setattr_prepare(dentry, attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 if (result < 0)
883 goto out;
884
885 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800886 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 goto out;
888
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800889 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 goto out;
891
892 if (((attr->ia_valid & ATTR_MODE) &&
893 (attr->ia_mode &
894 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
895 goto out;
896
897 info_mask = 0;
898 memset(&info, 0, sizeof(info));
899
900#if 1
901 if ((attr->ia_valid & ATTR_MODE) != 0)
902 {
903 umode_t newmode = attr->ia_mode;
904
905 info_mask |= DM_ATTRIBUTES;
906
907 if (S_ISDIR(inode->i_mode)) {
908 newmode &= server->m.dir_mode;
909 } else {
910#ifdef CONFIG_NCPFS_EXTRAS
911 if (server->m.flags & NCP_MOUNT_EXTRAS) {
912 /* any non-default execute bit set */
913 if (newmode & ~server->m.file_mode & S_IXUGO)
914 info.attributes |= aSHARED | aSYSTEM;
915 /* read for group/world and not in default file_mode */
916 else if (newmode & ~server->m.file_mode & S_IRUGO)
917 info.attributes |= aSHARED;
918 } else
919#endif
920 newmode &= server->m.file_mode;
921 }
922 if (newmode & S_IWUGO)
923 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
924 else
925 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
926
927#ifdef CONFIG_NCPFS_NFS_NS
928 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
929 result = ncp_modify_nfs_info(server,
930 NCP_FINFO(inode)->volNumber,
931 NCP_FINFO(inode)->dirEntNum,
932 attr->ia_mode, 0);
933 if (result != 0)
934 goto out;
935 info.attributes &= ~(aSHARED | aSYSTEM);
936 {
937 /* mark partial success */
938 struct iattr tmpattr;
939
940 tmpattr.ia_valid = ATTR_MODE;
941 tmpattr.ia_mode = attr->ia_mode;
942
Christoph Hellwig10257742010-06-04 11:30:02 +0200943 setattr_copy(inode, &tmpattr);
944 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 }
946 }
947#endif
948 }
949#endif
950
951 /* Do SIZE before attributes, otherwise mtime together with size does not work...
952 */
953 if ((attr->ia_valid & ATTR_SIZE) != 0) {
954 int written;
955
Joe Perches15a03ac2014-04-08 16:04:18 -0700956 ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
959 result = -EACCES;
960 goto out;
961 }
962 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
963 attr->ia_size, 0, "", &written);
964
965 /* According to ndir, the changes only take effect after
966 closing the file */
967 ncp_inode_close(inode);
968 result = ncp_make_closed(inode);
969 if (result)
970 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +0200971
972 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +0100973 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +0200974 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 }
976 }
977 if ((attr->ia_valid & ATTR_CTIME) != 0) {
978 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
979 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
980 &info.creationTime, &info.creationDate);
981 }
982 if ((attr->ia_valid & ATTR_MTIME) != 0) {
983 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
984 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
985 &info.modifyTime, &info.modifyDate);
986 }
987 if ((attr->ia_valid & ATTR_ATIME) != 0) {
988 __le16 dummy;
989 info_mask |= (DM_LAST_ACCESS_DATE);
990 ncp_date_unix2dos(attr->ia_atime.tv_sec,
991 &dummy, &info.lastAccessDate);
992 }
993 if (info_mask != 0) {
994 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
995 inode, info_mask, &info);
996 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
998 /* NetWare seems not to allow this. I
999 do not know why. So, just tell the
1000 user everything went fine. This is
1001 a terrible hack, but I do not know
1002 how to do this correctly. */
1003 result = 0;
1004 } else
1005 goto out;
1006 }
1007#ifdef CONFIG_NCPFS_STRONG
1008 if ((!result) && (info_mask & DM_ATTRIBUTES))
1009 NCP_FINFO(inode)->nwattr = info.attributes;
1010#endif
1011 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001012 if (result)
1013 goto out;
1014
1015 setattr_copy(inode, attr);
1016 mark_inode_dirty(inode);
1017
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001019 if (result > 0)
1020 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 return result;
1022}
1023
Al Viro3c26ff62010-07-25 11:46:36 +04001024static struct dentry *ncp_mount(struct file_system_type *fs_type,
1025 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026{
Al Viro3c26ff62010-07-25 11:46:36 +04001027 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028}
1029
1030static struct file_system_type ncp_fs_type = {
1031 .owner = THIS_MODULE,
1032 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001033 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001035 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001037MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039static int __init init_ncp_fs(void)
1040{
1041 int err;
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001042 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 err = init_inodecache();
1045 if (err)
1046 goto out1;
1047 err = register_filesystem(&ncp_fs_type);
1048 if (err)
1049 goto out;
1050 return 0;
1051out:
1052 destroy_inodecache();
1053out1:
1054 return err;
1055}
1056
1057static void __exit exit_ncp_fs(void)
1058{
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001059 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 unregister_filesystem(&ncp_fs_type);
1061 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062}
1063
1064module_init(init_ncp_fs)
1065module_exit(exit_ncp_fs)
1066MODULE_LICENSE("GPL");