blob: f6cf4c7e92b1b8c3cf7e7846aa4c968a24829b03 [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 Torvalds1da177e2005-04-16 15:20:36 -070016#include <asm/uaccess.h>
17#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 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 .readlink = generic_readlink,
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
550 sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
551 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;
Jens Axboef1970c72010-04-22 12:31:11 +0200557 sb->s_bdi = &server->bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 server = NCP_SBP(sb);
560 memset(server, 0, sizeof(*server));
561
Christoph Hellwigb4caecd2015-01-14 10:42:32 +0100562 error = bdi_setup_and_register(&server->bdi, "ncpfs");
Jens Axboef1970c72010-04-22 12:31:11 +0200563 if (error)
Djalal Harouni759c3612011-12-13 02:47:29 +0100564 goto out_fput;
Jens Axboef1970c72010-04-22 12:31:11 +0200565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 server->ncp_sock = sock;
567
568 if (data.info_fd != -1) {
Al Viro44ba84062014-03-06 17:41:01 -0500569 struct socket *info_sock = sockfd_lookup(data.info_fd, &error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 if (!info_sock)
Al Viro44ba84062014-03-06 17:41:01 -0500571 goto out_bdi;
572 server->info_sock = info_sock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 error = -EBADFD;
574 if (info_sock->type != SOCK_STREAM)
575 goto out_fput2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 }
577
578/* server->lock = 0; */
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800579 mutex_init(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 server->packet = NULL;
581/* server->buffer_size = 0; */
582/* server->conn_status = 0; */
583/* server->root_dentry = NULL; */
584/* server->root_setuped = 0; */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200585 mutex_init(&server->root_setup_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586#ifdef CONFIG_NCPFS_PACKET_SIGNING
587/* server->sign_wanted = 0; */
588/* server->sign_active = 0; */
589#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200590 init_rwsem(&server->auth_rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 server->auth.auth_type = NCP_AUTH_NONE;
592/* server->auth.object_name_len = 0; */
593/* server->auth.object_name = NULL; */
594/* server->auth.object_type = 0; */
595/* server->priv.len = 0; */
596/* server->priv.data = NULL; */
597
598 server->m = data;
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300599 /* Although anything producing this is buggy, it happens
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 now because of PATH_MAX changes.. */
601 if (server->m.time_out < 1) {
602 server->m.time_out = 10;
Joe Perchesb41f8b82014-04-08 16:04:14 -0700603 pr_info("You need to recompile your ncpfs utils..\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
605 server->m.time_out = server->m.time_out * HZ / 100;
606 server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
607 server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
608
609#ifdef CONFIG_NCPFS_NLS
610 /* load the default NLS charsets */
611 server->nls_vol = load_nls_default();
612 server->nls_io = load_nls_default();
613#endif /* CONFIG_NCPFS_NLS */
614
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200615 atomic_set(&server->dentry_ttl, 0); /* no caching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
617 INIT_LIST_HEAD(&server->tx.requests);
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800618 mutex_init(&server->rcv.creq_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 server->tx.creq = NULL;
620 server->rcv.creq = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 init_timer(&server->timeout_tm);
623#undef NCP_PACKET_SIZE
624#define NCP_PACKET_SIZE 131072
625 error = -ENOMEM;
626 server->packet_size = NCP_PACKET_SIZE;
627 server->packet = vmalloc(NCP_PACKET_SIZE);
628 if (server->packet == NULL)
629 goto out_nls;
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100630 server->txbuf = vmalloc(NCP_PACKET_SIZE);
631 if (server->txbuf == NULL)
632 goto out_packet;
633 server->rxbuf = vmalloc(NCP_PACKET_SIZE);
634 if (server->rxbuf == NULL)
635 goto out_txbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200637 lock_sock(sock->sk);
638 server->data_ready = sock->sk->sk_data_ready;
639 server->write_space = sock->sk->sk_write_space;
640 server->error_report = sock->sk->sk_error_report;
641 sock->sk->sk_user_data = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 sock->sk->sk_data_ready = ncp_tcp_data_ready;
643 sock->sk->sk_error_report = ncp_tcp_error_report;
644 if (sock->type == SOCK_STREAM) {
645 server->rcv.ptr = (unsigned char*)&server->rcv.buf;
646 server->rcv.len = 10;
647 server->rcv.state = 0;
David Howellsc4028952006-11-22 14:57:56 +0000648 INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
649 INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 sock->sk->sk_write_space = ncp_tcp_write_space;
651 } else {
David Howellsc4028952006-11-22 14:57:56 +0000652 INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
653 INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 server->timeout_tm.data = (unsigned long)server;
655 server->timeout_tm.function = ncpdgram_timeout_call;
656 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200657 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
659 ncp_lock_server(server);
660 error = ncp_connect(server);
661 ncp_unlock_server(server);
662 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100663 goto out_rxbuf;
Joe Perches15a03ac2014-04-08 16:04:18 -0700664 ncp_dbg(1, "NCP_SBP(sb) = %p\n", NCP_SBP(sb));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
667#ifdef CONFIG_NCPFS_PACKET_SIGNING
668 if (ncp_negotiate_size_and_options(server, default_bufsize,
669 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
670 {
671 if (options != NCP_DEFAULT_OPTIONS)
672 {
673 if (ncp_negotiate_size_and_options(server,
674 default_bufsize,
675 options & 2,
676 &(server->buffer_size), &options) != 0)
677
678 {
679 goto out_disconnect;
680 }
681 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200682 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (options & 2)
684 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200685 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 }
687 else
688#endif /* CONFIG_NCPFS_PACKET_SIGNING */
689 if (ncp_negotiate_buffersize(server, default_bufsize,
690 &(server->buffer_size)) != 0)
691 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700692 ncp_dbg(1, "bufsize = %d\n", server->buffer_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694 memset(&finfo, 0, sizeof(finfo));
695 finfo.i.attributes = aDIR;
696 finfo.i.dataStreamSize = 0; /* ignored */
697 finfo.i.dirEntNum = 0;
698 finfo.i.DosDirNum = 0;
699#ifdef CONFIG_NCPFS_SMALLDOS
700 finfo.i.NSCreator = NW_NS_DOS;
701#endif
702 finfo.volume = NCP_NUMBER_OF_VOLUMES;
703 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
704 finfo.i.creationTime = finfo.i.modifyTime
705 = cpu_to_le16(0x0000);
706 finfo.i.creationDate = finfo.i.modifyDate
707 = finfo.i.lastAccessDate
708 = cpu_to_le16(0x0C21);
709 finfo.i.nameLen = 0;
710 finfo.i.entryName[0] = '\0';
711
712 finfo.opened = 0;
713 finfo.ino = 2; /* tradition */
714
715 server->name_space[finfo.volume] = NW_NS_DOS;
716
717 error = -ENOMEM;
718 root_inode = ncp_iget(sb, &finfo);
719 if (!root_inode)
720 goto out_disconnect;
Joe Perchesd3b73ca2014-04-08 16:04:15 -0700721 ncp_dbg(1, "root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500722 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500724 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return 0;
726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727out_disconnect:
728 ncp_lock_server(server);
729 ncp_disconnect(server);
730 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100731out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100733 vfree(server->rxbuf);
734out_txbuf:
735 vfree(server->txbuf);
736out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 vfree(server->packet);
738out_nls:
739#ifdef CONFIG_NCPFS_NLS
740 unload_nls(server->nls_io);
741 unload_nls(server->nls_vol);
742#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200743 mutex_destroy(&server->rcv.creq_mutex);
744 mutex_destroy(&server->root_setup_lock);
745 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746out_fput2:
Al Viro44ba84062014-03-06 17:41:01 -0500747 if (server->info_sock)
748 sockfd_put(server->info_sock);
Jens Axboef1970c72010-04-22 12:31:11 +0200749out_bdi:
Djalal Harouni759c3612011-12-13 02:47:29 +0100750 bdi_destroy(&server->bdi);
751out_fput:
Al Viro44ba84062014-03-06 17:41:01 -0500752 sockfd_put(sock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800754 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 sb->s_fs_info = NULL;
756 kfree(server);
757 return error;
758}
759
Al Viro1dcddd42013-10-03 13:22:44 -0400760static void delayed_free(struct rcu_head *p)
761{
762 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
763#ifdef CONFIG_NCPFS_NLS
764 /* unload the NLS charsets */
765 unload_nls(server->nls_vol);
766 unload_nls(server->nls_io);
767#endif /* CONFIG_NCPFS_NLS */
768 kfree(server);
769}
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771static void ncp_put_super(struct super_block *sb)
772{
773 struct ncp_server *server = NCP_SBP(sb);
774
775 ncp_lock_server(server);
776 ncp_disconnect(server);
777 ncp_unlock_server(server);
778
779 ncp_stop_tasks(server);
780
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200781 mutex_destroy(&server->rcv.creq_mutex);
782 mutex_destroy(&server->root_setup_lock);
783 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Al Viro44ba84062014-03-06 17:41:01 -0500785 if (server->info_sock)
786 sockfd_put(server->info_sock);
787 sockfd_put(server->ncp_sock);
Eric W. Biederman21542272006-12-13 00:35:11 -0800788 kill_pid(server->m.wdog_pid, SIGTERM, 1);
789 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Jens Axboef1970c72010-04-22 12:31:11 +0200791 bdi_destroy(&server->bdi);
Pekka Enberg44db77f2006-01-14 13:21:12 -0800792 kfree(server->priv.data);
793 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100794 vfree(server->rxbuf);
795 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400797 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798}
799
David Howells726c3342006-06-23 02:02:58 -0700800static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801{
802 struct dentry* d;
803 struct inode* i;
804 struct ncp_inode_info* ni;
805 struct ncp_server* s;
806 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700807 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 int err;
809 __u8 dh;
810
811 d = sb->s_root;
812 if (!d) {
813 goto dflt;
814 }
David Howells2b0143b2015-03-17 22:25:59 +0000815 i = d_inode(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 if (!i) {
817 goto dflt;
818 }
819 ni = NCP_FINFO(i);
820 if (!ni) {
821 goto dflt;
822 }
823 s = NCP_SBP(sb);
824 if (!s) {
825 goto dflt;
826 }
827 if (!s->m.mounted_vol[0]) {
828 goto dflt;
829 }
830
831 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
832 if (err) {
833 goto dflt;
834 }
835 err = ncp_get_directory_info(s, dh, &vi);
836 ncp_dirhandle_free(s, dh);
837 if (err) {
838 goto dflt;
839 }
840 buf->f_type = NCP_SUPER_MAGIC;
841 buf->f_bsize = vi.sectors_per_block * 512;
842 buf->f_blocks = vi.total_blocks;
843 buf->f_bfree = vi.free_blocks;
844 buf->f_bavail = vi.free_blocks;
845 buf->f_files = vi.total_dir_entries;
846 buf->f_ffree = vi.available_dir_entries;
847 buf->f_namelen = 12;
848 return 0;
849
850 /* We cannot say how much disk space is left on a mounted
851 NetWare Server, because free space is distributed over
852 volumes, and the current user might have disk quotas. So
853 free space is not that simple to determine. Our decision
854 here is to err conservatively. */
855
856dflt:;
857 buf->f_type = NCP_SUPER_MAGIC;
858 buf->f_bsize = NCP_BLOCK_SIZE;
859 buf->f_blocks = 0;
860 buf->f_bfree = 0;
861 buf->f_bavail = 0;
862 buf->f_namelen = 12;
863 return 0;
864}
865
866int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
867{
David Howells2b0143b2015-03-17 22:25:59 +0000868 struct inode *inode = d_inode(dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 int result = 0;
870 __le32 info_mask;
871 struct nw_modify_dos_info info;
872 struct ncp_server *server;
873
874 result = -EIO;
875
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200877 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 goto out;
879
Al Viro338b2f52013-06-15 05:53:23 +0400880 result = -EPERM;
David Howells2b0143b2015-03-17 22:25:59 +0000881 if (IS_DEADDIR(d_inode(dentry)))
Al Viro338b2f52013-06-15 05:53:23 +0400882 goto out;
883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 /* ageing the dentry to force validation */
885 ncp_age_dentry(server, dentry);
886
Jan Kara31051c82016-05-26 16:55:18 +0200887 result = setattr_prepare(dentry, attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 if (result < 0)
889 goto out;
890
891 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800892 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 goto out;
894
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800895 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 goto out;
897
898 if (((attr->ia_valid & ATTR_MODE) &&
899 (attr->ia_mode &
900 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
901 goto out;
902
903 info_mask = 0;
904 memset(&info, 0, sizeof(info));
905
906#if 1
907 if ((attr->ia_valid & ATTR_MODE) != 0)
908 {
909 umode_t newmode = attr->ia_mode;
910
911 info_mask |= DM_ATTRIBUTES;
912
913 if (S_ISDIR(inode->i_mode)) {
914 newmode &= server->m.dir_mode;
915 } else {
916#ifdef CONFIG_NCPFS_EXTRAS
917 if (server->m.flags & NCP_MOUNT_EXTRAS) {
918 /* any non-default execute bit set */
919 if (newmode & ~server->m.file_mode & S_IXUGO)
920 info.attributes |= aSHARED | aSYSTEM;
921 /* read for group/world and not in default file_mode */
922 else if (newmode & ~server->m.file_mode & S_IRUGO)
923 info.attributes |= aSHARED;
924 } else
925#endif
926 newmode &= server->m.file_mode;
927 }
928 if (newmode & S_IWUGO)
929 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
930 else
931 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
932
933#ifdef CONFIG_NCPFS_NFS_NS
934 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
935 result = ncp_modify_nfs_info(server,
936 NCP_FINFO(inode)->volNumber,
937 NCP_FINFO(inode)->dirEntNum,
938 attr->ia_mode, 0);
939 if (result != 0)
940 goto out;
941 info.attributes &= ~(aSHARED | aSYSTEM);
942 {
943 /* mark partial success */
944 struct iattr tmpattr;
945
946 tmpattr.ia_valid = ATTR_MODE;
947 tmpattr.ia_mode = attr->ia_mode;
948
Christoph Hellwig10257742010-06-04 11:30:02 +0200949 setattr_copy(inode, &tmpattr);
950 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 }
952 }
953#endif
954 }
955#endif
956
957 /* Do SIZE before attributes, otherwise mtime together with size does not work...
958 */
959 if ((attr->ia_valid & ATTR_SIZE) != 0) {
960 int written;
961
Joe Perches15a03ac2014-04-08 16:04:18 -0700962 ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963
964 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
965 result = -EACCES;
966 goto out;
967 }
968 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
969 attr->ia_size, 0, "", &written);
970
971 /* According to ndir, the changes only take effect after
972 closing the file */
973 ncp_inode_close(inode);
974 result = ncp_make_closed(inode);
975 if (result)
976 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +0200977
978 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +0100979 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +0200980 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 }
982 }
983 if ((attr->ia_valid & ATTR_CTIME) != 0) {
984 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
985 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
986 &info.creationTime, &info.creationDate);
987 }
988 if ((attr->ia_valid & ATTR_MTIME) != 0) {
989 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
990 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
991 &info.modifyTime, &info.modifyDate);
992 }
993 if ((attr->ia_valid & ATTR_ATIME) != 0) {
994 __le16 dummy;
995 info_mask |= (DM_LAST_ACCESS_DATE);
996 ncp_date_unix2dos(attr->ia_atime.tv_sec,
997 &dummy, &info.lastAccessDate);
998 }
999 if (info_mask != 0) {
1000 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
1001 inode, info_mask, &info);
1002 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
1004 /* NetWare seems not to allow this. I
1005 do not know why. So, just tell the
1006 user everything went fine. This is
1007 a terrible hack, but I do not know
1008 how to do this correctly. */
1009 result = 0;
1010 } else
1011 goto out;
1012 }
1013#ifdef CONFIG_NCPFS_STRONG
1014 if ((!result) && (info_mask & DM_ATTRIBUTES))
1015 NCP_FINFO(inode)->nwattr = info.attributes;
1016#endif
1017 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001018 if (result)
1019 goto out;
1020
1021 setattr_copy(inode, attr);
1022 mark_inode_dirty(inode);
1023
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001025 if (result > 0)
1026 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 return result;
1028}
1029
Al Viro3c26ff62010-07-25 11:46:36 +04001030static struct dentry *ncp_mount(struct file_system_type *fs_type,
1031 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032{
Al Viro3c26ff62010-07-25 11:46:36 +04001033 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034}
1035
1036static struct file_system_type ncp_fs_type = {
1037 .owner = THIS_MODULE,
1038 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001039 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001041 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001043MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
1045static int __init init_ncp_fs(void)
1046{
1047 int err;
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001048 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 err = init_inodecache();
1051 if (err)
1052 goto out1;
1053 err = register_filesystem(&ncp_fs_type);
1054 if (err)
1055 goto out;
1056 return 0;
1057out:
1058 destroy_inodecache();
1059out1:
1060 return err;
1061}
1062
1063static void __exit exit_ncp_fs(void)
1064{
Joe Perchesd3b73ca2014-04-08 16:04:15 -07001065 ncp_dbg(1, "called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 unregister_filesystem(&ncp_fs_type);
1067 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068}
1069
1070module_init(init_ncp_fs)
1071module_exit(exit_ncp_fs)
1072MODULE_LICENSE("GPL");