blob: 7eb89c23c8470b75046e266afb2e593b7786d1fc [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>
Nick Piggin34286d62011-01-07 17:49:57 +110033#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <net/sock.h>
36
Al Viro32c419d2011-01-12 17:37:47 -050037#include "ncp_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "getopt.h"
39
Miklos Szeredi564cd132008-02-08 04:21:46 -080040#define NCP_DEFAULT_FILE_MODE 0600
41#define NCP_DEFAULT_DIR_MODE 0700
42#define NCP_DEFAULT_TIME_OUT 10
43#define NCP_DEFAULT_RETRY_COUNT 20
44
Al Viro94ee8492010-06-07 00:45:56 -040045static void ncp_evict_inode(struct inode *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046static void ncp_put_super(struct super_block *);
David Howells726c3342006-06-23 02:02:58 -070047static int ncp_statfs(struct dentry *, struct kstatfs *);
Al Viro34c80b12011-12-08 21:32:45 -050048static int ncp_show_options(struct seq_file *, struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Christoph Lametere18b8902006-12-06 20:33:20 -080050static struct kmem_cache * ncp_inode_cachep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52static struct inode *ncp_alloc_inode(struct super_block *sb)
53{
54 struct ncp_inode_info *ei;
Christoph Lametere94b1762006-12-06 20:33:17 -080055 ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 if (!ei)
57 return NULL;
58 return &ei->vfs_inode;
59}
60
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110061static void ncp_i_callback(struct rcu_head *head)
62{
63 struct inode *inode = container_of(head, struct inode, i_rcu);
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110064 kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
65}
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067static void ncp_destroy_inode(struct inode *inode)
68{
Nick Pigginfa0d7e3d2011-01-07 17:49:49 +110069 call_rcu(&inode->i_rcu, ncp_i_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070}
71
Alexey Dobriyan51cc5062008-07-25 19:45:34 -070072static void init_once(void *foo)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
74 struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
75
Christoph Lametera35afb82007-05-16 22:10:57 -070076 mutex_init(&ei->open_mutex);
77 inode_init_once(&ei->vfs_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078}
Paul Mundt20c2df82007-07-20 10:11:58 +090079
Linus Torvalds1da177e2005-04-16 15:20:36 -070080static int init_inodecache(void)
81{
82 ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
83 sizeof(struct ncp_inode_info),
Paul Jacksonfffb60f2006-03-24 03:16:06 -080084 0, (SLAB_RECLAIM_ACCOUNT|
Vladimir Davydov5d097052016-01-14 15:18:21 -080085 SLAB_MEM_SPREAD|SLAB_ACCOUNT),
Paul Mundt20c2df82007-07-20 10:11:58 +090086 init_once);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if (ncp_inode_cachep == NULL)
88 return -ENOMEM;
89 return 0;
90}
91
92static void destroy_inodecache(void)
93{
Kirill A. Shutemov8c0a8532012-09-26 11:33:07 +100094 /*
95 * Make sure all delayed rcu free inodes are flushed before we
96 * destroy cache.
97 */
98 rcu_barrier();
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -070099 kmem_cache_destroy(ncp_inode_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
102static int ncp_remount(struct super_block *sb, int *flags, char* data)
103{
Theodore Ts'o02b99842014-03-13 10:14:33 -0400104 sync_filesystem(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 *flags |= MS_NODIRATIME;
106 return 0;
107}
108
Josef 'Jeff' Sipekee9b6d62007-02-12 00:55:41 -0800109static const struct super_operations ncp_sops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
111 .alloc_inode = ncp_alloc_inode,
112 .destroy_inode = ncp_destroy_inode,
113 .drop_inode = generic_delete_inode,
Al Viro94ee8492010-06-07 00:45:56 -0400114 .evict_inode = ncp_evict_inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 .put_super = ncp_put_super,
116 .statfs = ncp_statfs,
117 .remount_fs = ncp_remount,
Miklos Szeredi564cd132008-02-08 04:21:46 -0800118 .show_options = ncp_show_options,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119};
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * Fill in the ncpfs-specific information in the inode.
123 */
124static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo)
125{
126 NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
127 NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
128 NCP_FINFO(inode)->volNumber = nwinfo->volume;
129}
130
131void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
132{
133 ncp_update_dirent(inode, nwinfo);
134 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
135 NCP_FINFO(inode)->access = nwinfo->access;
136 memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
137 sizeof(nwinfo->file_handle));
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700138 ncp_dbg(1, "updated %s, volnum=%d, dirent=%u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
140 NCP_FINFO(inode)->dirEntNum);
141}
142
143static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi)
144{
145 /* NFS namespace mode overrides others if it's set. */
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700146 ncp_dbg(1, "(%s) nfs.mode=0%o\n", nwi->entryName, nwi->nfs.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 if (nwi->nfs.mode) {
148 /* XXX Security? */
149 inode->i_mode = nwi->nfs.mode;
150 }
151
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200152 inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate);
155 inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate);
156 inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate);
157 inode->i_atime.tv_nsec = 0;
158 inode->i_mtime.tv_nsec = 0;
159 inode->i_ctime.tv_nsec = 0;
160}
161
162static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo)
163{
164 struct nw_info_struct *nwi = &nwinfo->i;
165 struct ncp_server *server = NCP_SERVER(inode);
166
167 if (nwi->attributes & aDIR) {
168 inode->i_mode = server->m.dir_mode;
169 /* for directories dataStreamSize seems to be some
170 Object ID ??? */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200171 i_size_write(inode, NCP_BLOCK_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 } else {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200173 u32 size;
174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 inode->i_mode = server->m.file_mode;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200176 size = le32_to_cpu(nwi->dataStreamSize);
177 i_size_write(inode, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178#ifdef CONFIG_NCPFS_EXTRAS
179 if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
180 && (nwi->attributes & aSHARED)) {
181 switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
182 case aHIDDEN:
183 if (server->m.flags & NCP_MOUNT_SYMLINKS) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200184 if (/* (size >= NCP_MIN_SYMLINK_SIZE)
185 && */ (size <= NCP_MAX_SYMLINK_SIZE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
187 NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK;
188 break;
189 }
190 }
191 /* FALLTHROUGH */
192 case 0:
193 if (server->m.flags & NCP_MOUNT_EXTRAS)
194 inode->i_mode |= S_IRUGO;
195 break;
196 case aSYSTEM:
197 if (server->m.flags & NCP_MOUNT_EXTRAS)
198 inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO;
199 break;
200 /* case aSYSTEM|aHIDDEN: */
201 default:
202 /* reserved combination */
203 break;
204 }
205 }
206#endif
207 }
208 if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO;
209}
210
211void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
212{
213 NCP_FINFO(inode)->flags = 0;
214 if (!atomic_read(&NCP_FINFO(inode)->opened)) {
215 NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
216 ncp_update_attrs(inode, nwinfo);
217 }
218
219 ncp_update_dates(inode, &nwinfo->i);
220 ncp_update_dirent(inode, nwinfo);
221}
222
223/*
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200224 * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 */
226static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
227{
228 struct ncp_server *server = NCP_SERVER(inode);
229
230 NCP_FINFO(inode)->flags = 0;
231
232 ncp_update_attrs(inode, nwinfo);
233
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700234 ncp_dbg(2, "inode->i_mode = %u\n", inode->i_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Miklos Szeredibfe86842011-10-28 14:13:29 +0200236 set_nlink(inode, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 inode->i_uid = server->m.uid;
238 inode->i_gid = server->m.gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240 ncp_update_dates(inode, &nwinfo->i);
241 ncp_update_inode(inode, nwinfo);
242}
243
244#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -0800245static const struct inode_operations ncp_symlink_inode_operations = {
Al Viro6b255392015-11-17 10:20:54 -0500246 .get_link = page_get_link,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 .setattr = ncp_notify_change,
248};
249#endif
250
251/*
252 * Get a new inode.
253 */
254struct inode *
255ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
256{
257 struct inode *inode;
258
259 if (info == NULL) {
Joe Perchesb41f8b82014-04-08 16:04:14 -0700260 pr_err("%s: info is NULL\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 return NULL;
262 }
263
264 inode = new_inode(sb);
265 if (inode) {
266 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
267
268 inode->i_ino = info->ino;
269 ncp_set_attr(inode, info);
270 if (S_ISREG(inode->i_mode)) {
271 inode->i_op = &ncp_file_inode_operations;
272 inode->i_fop = &ncp_file_operations;
273 } else if (S_ISDIR(inode->i_mode)) {
274 inode->i_op = &ncp_dir_inode_operations;
275 inode->i_fop = &ncp_dir_operations;
276#ifdef CONFIG_NCPFS_NFS_NS
277 } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
278 init_special_inode(inode, inode->i_mode,
279 new_decode_dev(info->i.nfs.rdev));
280#endif
281#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
282 } else if (S_ISLNK(inode->i_mode)) {
283 inode->i_op = &ncp_symlink_inode_operations;
Al Viro21fc61c2015-11-17 01:07:57 -0500284 inode_nohighmem(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 inode->i_data.a_ops = &ncp_symlink_aops;
286#endif
287 } else {
288 make_bad_inode(inode);
289 }
290 insert_inode_hash(inode);
291 } else
Joe Perchesb41f8b82014-04-08 16:04:14 -0700292 pr_err("%s: iget failed!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 return inode;
294}
295
296static void
Al Viro94ee8492010-06-07 00:45:56 -0400297ncp_evict_inode(struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
Johannes Weiner91b0abe2014-04-03 14:47:49 -0700299 truncate_inode_pages_final(&inode->i_data);
Jan Karadbd57682012-05-03 14:48:02 +0200300 clear_inode(inode);
Mark Fashehfef26652005-09-09 13:01:31 -0700301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (S_ISDIR(inode->i_mode)) {
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700303 ncp_dbg(2, "put directory %ld\n", inode->i_ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 }
305
306 if (ncp_make_closed(inode) != 0) {
307 /* We can't do anything but complain. */
Joe Perchesb41f8b82014-04-08 16:04:14 -0700308 pr_err("%s: could not close\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312static void ncp_stop_tasks(struct ncp_server *server) {
313 struct sock* sk = server->ncp_sock->sk;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200314
315 lock_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 sk->sk_error_report = server->error_report;
317 sk->sk_data_ready = server->data_ready;
318 sk->sk_write_space = server->write_space;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200319 release_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 del_timer_sync(&server->timeout_tm);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100321
Tejun Heo43829732012-08-20 14:51:24 -0700322 flush_work(&server->rcv.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100323 if (sk->sk_socket->type == SOCK_STREAM)
Tejun Heo43829732012-08-20 14:51:24 -0700324 flush_work(&server->tx.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100325 else
Tejun Heo43829732012-08-20 14:51:24 -0700326 flush_work(&server->timeout_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
Al Viro34c80b12011-12-08 21:32:45 -0500329static int ncp_show_options(struct seq_file *seq, struct dentry *root)
Miklos Szeredi564cd132008-02-08 04:21:46 -0800330{
Al Viro34c80b12011-12-08 21:32:45 -0500331 struct ncp_server *server = NCP_SBP(root->d_sb);
Miklos Szeredi564cd132008-02-08 04:21:46 -0800332 unsigned int tmp;
333
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800334 if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
335 seq_printf(seq, ",uid=%u",
336 from_kuid_munged(&init_user_ns, server->m.uid));
337 if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
338 seq_printf(seq, ",gid=%u",
339 from_kgid_munged(&init_user_ns, server->m.gid));
340 if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
341 seq_printf(seq, ",owner=%u",
342 from_kuid_munged(&init_user_ns, server->m.mounted_uid));
Miklos Szeredi564cd132008-02-08 04:21:46 -0800343 tmp = server->m.file_mode & S_IALLUGO;
344 if (tmp != NCP_DEFAULT_FILE_MODE)
345 seq_printf(seq, ",mode=0%o", tmp);
346 tmp = server->m.dir_mode & S_IALLUGO;
347 if (tmp != NCP_DEFAULT_DIR_MODE)
348 seq_printf(seq, ",dirmode=0%o", tmp);
349 if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
350 tmp = server->m.time_out * 100 / HZ;
351 seq_printf(seq, ",timeout=%u", tmp);
352 }
353 if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
354 seq_printf(seq, ",retry=%u", server->m.retry_count);
355 if (server->m.flags != 0)
356 seq_printf(seq, ",flags=%lu", server->m.flags);
357 if (server->m.wdog_pid != NULL)
358 seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
359
360 return 0;
361}
362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363static const struct ncp_option ncp_opts[] = {
364 { "uid", OPT_INT, 'u' },
365 { "gid", OPT_INT, 'g' },
366 { "owner", OPT_INT, 'o' },
367 { "mode", OPT_INT, 'm' },
368 { "dirmode", OPT_INT, 'd' },
369 { "timeout", OPT_INT, 't' },
370 { "retry", OPT_INT, 'r' },
371 { "flags", OPT_INT, 'f' },
372 { "wdogpid", OPT_INT, 'w' },
373 { "ncpfd", OPT_INT, 'n' },
374 { "infofd", OPT_INT, 'i' }, /* v5 */
375 { "version", OPT_INT, 'v' },
376 { NULL, 0, 0 } };
377
378static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
379 int optval;
380 char *optarg;
381 unsigned long optint;
382 int version = 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800383 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 data->flags = 0;
386 data->int_flags = 0;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800387 data->mounted_uid = GLOBAL_ROOT_UID;
Eric W. Biederman21542272006-12-13 00:35:11 -0800388 data->wdog_pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 data->ncp_fd = ~0;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800390 data->time_out = NCP_DEFAULT_TIME_OUT;
391 data->retry_count = NCP_DEFAULT_RETRY_COUNT;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800392 data->uid = GLOBAL_ROOT_UID;
393 data->gid = GLOBAL_ROOT_GID;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800394 data->file_mode = NCP_DEFAULT_FILE_MODE;
395 data->dir_mode = NCP_DEFAULT_DIR_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 data->info_fd = -1;
397 data->mounted_vol[0] = 0;
398
399 while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
Eric W. Biederman1de24122006-12-13 00:35:13 -0800400 ret = optval;
401 if (ret < 0)
402 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 switch (optval) {
404 case 'u':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800405 data->uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800406 if (!uid_valid(data->uid)) {
407 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800408 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800409 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 break;
411 case 'g':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800412 data->gid = make_kgid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800413 if (!gid_valid(data->gid)) {
414 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800415 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 break;
418 case 'o':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800419 data->mounted_uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800420 if (!uid_valid(data->mounted_uid)) {
421 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800422 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 break;
425 case 'm':
426 data->file_mode = optint;
427 break;
428 case 'd':
429 data->dir_mode = optint;
430 break;
431 case 't':
432 data->time_out = optint;
433 break;
434 case 'r':
435 data->retry_count = optint;
436 break;
437 case 'f':
438 data->flags = optint;
439 break;
440 case 'w':
Eric W. Biederman21542272006-12-13 00:35:11 -0800441 data->wdog_pid = find_get_pid(optint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 break;
443 case 'n':
444 data->ncp_fd = optint;
445 break;
446 case 'i':
447 data->info_fd = optint;
448 break;
449 case 'v':
Eric W. Biederman1de24122006-12-13 00:35:13 -0800450 ret = -ECHRNG;
451 if (optint < NCP_MOUNT_VERSION_V4)
452 goto err;
453 if (optint > NCP_MOUNT_VERSION_V5)
454 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 version = optint;
456 break;
457
458 }
459 }
460 return 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800461err:
462 put_pid(data->wdog_pid);
463 data->wdog_pid = NULL;
464 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
468{
469 struct ncp_mount_data_kernel data;
470 struct ncp_server *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 struct inode *root_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 struct socket *sock;
473 int error;
474 int default_bufsize;
475#ifdef CONFIG_NCPFS_PACKET_SIGNING
476 int options;
477#endif
478 struct ncp_entry_info finfo;
479
Andrew Morton2a5cac12011-05-24 17:13:42 -0700480 memset(&data, 0, sizeof(data));
Panagiotis Issarisf8314dc2006-09-27 01:49:37 -0700481 server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if (!server)
483 return -ENOMEM;
484 sb->s_fs_info = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 error = -EFAULT;
487 if (raw_data == NULL)
488 goto out;
489 switch (*(int*)raw_data) {
490 case NCP_MOUNT_VERSION:
491 {
492 struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
493
494 data.flags = md->flags;
495 data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800496 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800497 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 data.ncp_fd = md->ncp_fd;
499 data.time_out = md->time_out;
500 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800501 data.uid = make_kuid(current_user_ns(), md->uid);
502 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 data.file_mode = md->file_mode;
504 data.dir_mode = md->dir_mode;
505 data.info_fd = -1;
506 memcpy(data.mounted_vol, md->mounted_vol,
507 NCP_VOLNAME_LEN+1);
508 }
509 break;
510 case NCP_MOUNT_VERSION_V4:
511 {
512 struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
513
514 data.flags = md->flags;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800515 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800516 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 data.ncp_fd = md->ncp_fd;
518 data.time_out = md->time_out;
519 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800520 data.uid = make_kuid(current_user_ns(), md->uid);
521 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 data.file_mode = md->file_mode;
523 data.dir_mode = md->dir_mode;
524 data.info_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
526 break;
527 default:
528 error = -ECHRNG;
529 if (memcmp(raw_data, "vers", 4) == 0) {
530 error = ncp_parse_options(&data, raw_data);
531 }
532 if (error)
533 goto out;
534 break;
535 }
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800536 error = -EINVAL;
537 if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
538 !gid_valid(data.gid))
539 goto out;
Al Viro44ba84062014-03-06 17:41:01 -0500540 sock = sockfd_lookup(data.ncp_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 if (!sock)
Al Viro44ba84062014-03-06 17:41:01 -0500542 goto out;
543
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if (sock->type == SOCK_STREAM)
545 default_bufsize = 0xF000;
546 else
547 default_bufsize = 1024;
548
549 sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
550 sb->s_maxbytes = 0xFFFFFFFFU;
551 sb->s_blocksize = 1024; /* Eh... Is this correct? */
552 sb->s_blocksize_bits = 10;
553 sb->s_magic = NCP_SUPER_MAGIC;
554 sb->s_op = &ncp_sops;
Al Viro0378c402011-01-12 17:25:03 -0500555 sb->s_d_op = &ncp_dentry_operations;
Jens Axboef1970c72010-04-22 12:31:11 +0200556 sb->s_bdi = &server->bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 server = NCP_SBP(sb);
559 memset(server, 0, sizeof(*server));
560
Christoph Hellwigb4caecd2015-01-14 10:42:32 +0100561 error = bdi_setup_and_register(&server->bdi, "ncpfs");
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)
Al Viro44ba84062014-03-06 17:41:01 -0500570 goto out_bdi;
571 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
621 init_timer(&server->timeout_tm);
622#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 server->timeout_tm.data = (unsigned long)server;
654 server->timeout_tm.function = ncpdgram_timeout_call;
655 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200656 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 ncp_lock_server(server);
659 error = ncp_connect(server);
660 ncp_unlock_server(server);
661 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100662 goto out_rxbuf;
Joe Perches15a03ac2014-04-08 16:04:18 -0700663 ncp_dbg(1, "NCP_SBP(sb) = %p\n", NCP_SBP(sb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
665 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
666#ifdef CONFIG_NCPFS_PACKET_SIGNING
667 if (ncp_negotiate_size_and_options(server, default_bufsize,
668 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
669 {
670 if (options != NCP_DEFAULT_OPTIONS)
671 {
672 if (ncp_negotiate_size_and_options(server,
673 default_bufsize,
674 options & 2,
675 &(server->buffer_size), &options) != 0)
676
677 {
678 goto out_disconnect;
679 }
680 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200681 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 if (options & 2)
683 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200684 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 }
686 else
687#endif /* CONFIG_NCPFS_PACKET_SIGNING */
688 if (ncp_negotiate_buffersize(server, default_bufsize,
689 &(server->buffer_size)) != 0)
690 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700691 ncp_dbg(1, "bufsize = %d\n", server->buffer_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
693 memset(&finfo, 0, sizeof(finfo));
694 finfo.i.attributes = aDIR;
695 finfo.i.dataStreamSize = 0; /* ignored */
696 finfo.i.dirEntNum = 0;
697 finfo.i.DosDirNum = 0;
698#ifdef CONFIG_NCPFS_SMALLDOS
699 finfo.i.NSCreator = NW_NS_DOS;
700#endif
701 finfo.volume = NCP_NUMBER_OF_VOLUMES;
702 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
703 finfo.i.creationTime = finfo.i.modifyTime
704 = cpu_to_le16(0x0000);
705 finfo.i.creationDate = finfo.i.modifyDate
706 = finfo.i.lastAccessDate
707 = cpu_to_le16(0x0C21);
708 finfo.i.nameLen = 0;
709 finfo.i.entryName[0] = '\0';
710
711 finfo.opened = 0;
712 finfo.ino = 2; /* tradition */
713
714 server->name_space[finfo.volume] = NW_NS_DOS;
715
716 error = -ENOMEM;
717 root_inode = ncp_iget(sb, &finfo);
718 if (!root_inode)
719 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700720 ncp_dbg(1, "root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500721 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500723 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return 0;
725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726out_disconnect:
727 ncp_lock_server(server);
728 ncp_disconnect(server);
729 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100730out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100732 vfree(server->rxbuf);
733out_txbuf:
734 vfree(server->txbuf);
735out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 vfree(server->packet);
737out_nls:
738#ifdef CONFIG_NCPFS_NLS
739 unload_nls(server->nls_io);
740 unload_nls(server->nls_vol);
741#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200742 mutex_destroy(&server->rcv.creq_mutex);
743 mutex_destroy(&server->root_setup_lock);
744 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745out_fput2:
Al Viro44ba84062014-03-06 17:41:01 -0500746 if (server->info_sock)
747 sockfd_put(server->info_sock);
Jens Axboef1970c72010-04-22 12:31:11 +0200748out_bdi:
Djalal Harouni759c3612011-12-13 02:47:29 +0100749 bdi_destroy(&server->bdi);
750out_fput:
Al Viro44ba84062014-03-06 17:41:01 -0500751 sockfd_put(sock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800753 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 sb->s_fs_info = NULL;
755 kfree(server);
756 return error;
757}
758
Al Viro1dcddd42013-10-03 13:22:44 -0400759static void delayed_free(struct rcu_head *p)
760{
761 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
762#ifdef CONFIG_NCPFS_NLS
763 /* unload the NLS charsets */
764 unload_nls(server->nls_vol);
765 unload_nls(server->nls_io);
766#endif /* CONFIG_NCPFS_NLS */
767 kfree(server);
768}
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770static void ncp_put_super(struct super_block *sb)
771{
772 struct ncp_server *server = NCP_SBP(sb);
773
774 ncp_lock_server(server);
775 ncp_disconnect(server);
776 ncp_unlock_server(server);
777
778 ncp_stop_tasks(server);
779
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200780 mutex_destroy(&server->rcv.creq_mutex);
781 mutex_destroy(&server->root_setup_lock);
782 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
Al Viro44ba84062014-03-06 17:41:01 -0500784 if (server->info_sock)
785 sockfd_put(server->info_sock);
786 sockfd_put(server->ncp_sock);
Eric W. Biederman21542272006-12-13 00:35:11 -0800787 kill_pid(server->m.wdog_pid, SIGTERM, 1);
788 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Jens Axboef1970c72010-04-22 12:31:11 +0200790 bdi_destroy(&server->bdi);
Pekka Enberg44db77f2006-01-14 13:21:12 -0800791 kfree(server->priv.data);
792 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100793 vfree(server->rxbuf);
794 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400796 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797}
798
David Howells726c3342006-06-23 02:02:58 -0700799static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
801 struct dentry* d;
802 struct inode* i;
803 struct ncp_inode_info* ni;
804 struct ncp_server* s;
805 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700806 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 int err;
808 __u8 dh;
809
810 d = sb->s_root;
811 if (!d) {
812 goto dflt;
813 }
David Howells2b0143b2015-03-17 22:25:59 +0000814 i = d_inode(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if (!i) {
816 goto dflt;
817 }
818 ni = NCP_FINFO(i);
819 if (!ni) {
820 goto dflt;
821 }
822 s = NCP_SBP(sb);
823 if (!s) {
824 goto dflt;
825 }
826 if (!s->m.mounted_vol[0]) {
827 goto dflt;
828 }
829
830 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
831 if (err) {
832 goto dflt;
833 }
834 err = ncp_get_directory_info(s, dh, &vi);
835 ncp_dirhandle_free(s, dh);
836 if (err) {
837 goto dflt;
838 }
839 buf->f_type = NCP_SUPER_MAGIC;
840 buf->f_bsize = vi.sectors_per_block * 512;
841 buf->f_blocks = vi.total_blocks;
842 buf->f_bfree = vi.free_blocks;
843 buf->f_bavail = vi.free_blocks;
844 buf->f_files = vi.total_dir_entries;
845 buf->f_ffree = vi.available_dir_entries;
846 buf->f_namelen = 12;
847 return 0;
848
849 /* We cannot say how much disk space is left on a mounted
850 NetWare Server, because free space is distributed over
851 volumes, and the current user might have disk quotas. So
852 free space is not that simple to determine. Our decision
853 here is to err conservatively. */
854
855dflt:;
856 buf->f_type = NCP_SUPER_MAGIC;
857 buf->f_bsize = NCP_BLOCK_SIZE;
858 buf->f_blocks = 0;
859 buf->f_bfree = 0;
860 buf->f_bavail = 0;
861 buf->f_namelen = 12;
862 return 0;
863}
864
865int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
866{
David Howells2b0143b2015-03-17 22:25:59 +0000867 struct inode *inode = d_inode(dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 int result = 0;
869 __le32 info_mask;
870 struct nw_modify_dos_info info;
871 struct ncp_server *server;
872
873 result = -EIO;
874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200876 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 goto out;
878
Al Viro338b2f52013-06-15 05:53:23 +0400879 result = -EPERM;
David Howells2b0143b2015-03-17 22:25:59 +0000880 if (IS_DEADDIR(d_inode(dentry)))
Al Viro338b2f52013-06-15 05:53:23 +0400881 goto out;
882
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 /* ageing the dentry to force validation */
884 ncp_age_dentry(server, dentry);
885
Jan Kara31051c82016-05-26 16:55:18 +0200886 result = setattr_prepare(dentry, attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 if (result < 0)
888 goto out;
889
890 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800891 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 goto out;
893
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800894 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 goto out;
896
897 if (((attr->ia_valid & ATTR_MODE) &&
898 (attr->ia_mode &
899 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
900 goto out;
901
902 info_mask = 0;
903 memset(&info, 0, sizeof(info));
904
905#if 1
906 if ((attr->ia_valid & ATTR_MODE) != 0)
907 {
908 umode_t newmode = attr->ia_mode;
909
910 info_mask |= DM_ATTRIBUTES;
911
912 if (S_ISDIR(inode->i_mode)) {
913 newmode &= server->m.dir_mode;
914 } else {
915#ifdef CONFIG_NCPFS_EXTRAS
916 if (server->m.flags & NCP_MOUNT_EXTRAS) {
917 /* any non-default execute bit set */
918 if (newmode & ~server->m.file_mode & S_IXUGO)
919 info.attributes |= aSHARED | aSYSTEM;
920 /* read for group/world and not in default file_mode */
921 else if (newmode & ~server->m.file_mode & S_IRUGO)
922 info.attributes |= aSHARED;
923 } else
924#endif
925 newmode &= server->m.file_mode;
926 }
927 if (newmode & S_IWUGO)
928 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
929 else
930 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
931
932#ifdef CONFIG_NCPFS_NFS_NS
933 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
934 result = ncp_modify_nfs_info(server,
935 NCP_FINFO(inode)->volNumber,
936 NCP_FINFO(inode)->dirEntNum,
937 attr->ia_mode, 0);
938 if (result != 0)
939 goto out;
940 info.attributes &= ~(aSHARED | aSYSTEM);
941 {
942 /* mark partial success */
943 struct iattr tmpattr;
944
945 tmpattr.ia_valid = ATTR_MODE;
946 tmpattr.ia_mode = attr->ia_mode;
947
Christoph Hellwig10257742010-06-04 11:30:02 +0200948 setattr_copy(inode, &tmpattr);
949 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
951 }
952#endif
953 }
954#endif
955
956 /* Do SIZE before attributes, otherwise mtime together with size does not work...
957 */
958 if ((attr->ia_valid & ATTR_SIZE) != 0) {
959 int written;
960
Joe Perches15a03ac2014-04-08 16:04:18 -0700961 ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962
963 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
964 result = -EACCES;
965 goto out;
966 }
967 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
968 attr->ia_size, 0, "", &written);
969
970 /* According to ndir, the changes only take effect after
971 closing the file */
972 ncp_inode_close(inode);
973 result = ncp_make_closed(inode);
974 if (result)
975 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +0200976
977 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +0100978 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +0200979 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 }
981 }
982 if ((attr->ia_valid & ATTR_CTIME) != 0) {
983 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
984 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
985 &info.creationTime, &info.creationDate);
986 }
987 if ((attr->ia_valid & ATTR_MTIME) != 0) {
988 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
989 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
990 &info.modifyTime, &info.modifyDate);
991 }
992 if ((attr->ia_valid & ATTR_ATIME) != 0) {
993 __le16 dummy;
994 info_mask |= (DM_LAST_ACCESS_DATE);
995 ncp_date_unix2dos(attr->ia_atime.tv_sec,
996 &dummy, &info.lastAccessDate);
997 }
998 if (info_mask != 0) {
999 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
1000 inode, info_mask, &info);
1001 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
1003 /* NetWare seems not to allow this. I
1004 do not know why. So, just tell the
1005 user everything went fine. This is
1006 a terrible hack, but I do not know
1007 how to do this correctly. */
1008 result = 0;
1009 } else
1010 goto out;
1011 }
1012#ifdef CONFIG_NCPFS_STRONG
1013 if ((!result) && (info_mask & DM_ATTRIBUTES))
1014 NCP_FINFO(inode)->nwattr = info.attributes;
1015#endif
1016 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001017 if (result)
1018 goto out;
1019
1020 setattr_copy(inode, attr);
1021 mark_inode_dirty(inode);
1022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001024 if (result > 0)
1025 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 return result;
1027}
1028
Al Viro3c26ff62010-07-25 11:46:36 +04001029static struct dentry *ncp_mount(struct file_system_type *fs_type,
1030 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031{
Al Viro3c26ff62010-07-25 11:46:36 +04001032 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033}
1034
1035static struct file_system_type ncp_fs_type = {
1036 .owner = THIS_MODULE,
1037 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001038 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001040 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001042MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
1044static int __init init_ncp_fs(void)
1045{
1046 int err;
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001047 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 err = init_inodecache();
1050 if (err)
1051 goto out1;
1052 err = register_filesystem(&ncp_fs_type);
1053 if (err)
1054 goto out;
1055 return 0;
1056out:
1057 destroy_inodecache();
1058out1:
1059 return err;
1060}
1061
1062static void __exit exit_ncp_fs(void)
1063{
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001064 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 unregister_filesystem(&ncp_fs_type);
1066 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067}
1068
1069module_init(init_ncp_fs)
1070module_exit(exit_ncp_fs)
1071MODULE_LICENSE("GPL");