blob: 3277fc1329592268de17937c71b7eb1529e4c393 [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|
85 SLAB_MEM_SPREAD),
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));
138 DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n",
139 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. */
146 DPRINTK(KERN_DEBUG "ncp_update_dates_and_mode: (%s) nfs.mode=0%o\n",
147 nwi->entryName, nwi->nfs.mode);
148 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
235 DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
236
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 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 .readlink = generic_readlink,
248 .follow_link = page_follow_link_light,
249 .put_link = page_put_link,
250 .setattr = ncp_notify_change,
251};
252#endif
253
254/*
255 * Get a new inode.
256 */
257struct inode *
258ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
259{
260 struct inode *inode;
261
262 if (info == NULL) {
Joe Perchesb41f8b82014-04-08 16:04:14 -0700263 pr_err("%s: info is NULL\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 return NULL;
265 }
266
267 inode = new_inode(sb);
268 if (inode) {
269 atomic_set(&NCP_FINFO(inode)->opened, info->opened);
270
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200271 inode->i_mapping->backing_dev_info = sb->s_bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 inode->i_ino = info->ino;
273 ncp_set_attr(inode, info);
274 if (S_ISREG(inode->i_mode)) {
275 inode->i_op = &ncp_file_inode_operations;
276 inode->i_fop = &ncp_file_operations;
277 } else if (S_ISDIR(inode->i_mode)) {
278 inode->i_op = &ncp_dir_inode_operations;
279 inode->i_fop = &ncp_dir_operations;
280#ifdef CONFIG_NCPFS_NFS_NS
281 } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
282 init_special_inode(inode, inode->i_mode,
283 new_decode_dev(info->i.nfs.rdev));
284#endif
285#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
286 } else if (S_ISLNK(inode->i_mode)) {
287 inode->i_op = &ncp_symlink_inode_operations;
288 inode->i_data.a_ops = &ncp_symlink_aops;
289#endif
290 } else {
291 make_bad_inode(inode);
292 }
293 insert_inode_hash(inode);
294 } else
Joe Perchesb41f8b82014-04-08 16:04:14 -0700295 pr_err("%s: iget failed!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 return inode;
297}
298
299static void
Al Viro94ee8492010-06-07 00:45:56 -0400300ncp_evict_inode(struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301{
Johannes Weiner91b0abe2014-04-03 14:47:49 -0700302 truncate_inode_pages_final(&inode->i_data);
Jan Karadbd57682012-05-03 14:48:02 +0200303 clear_inode(inode);
Mark Fashehfef26652005-09-09 13:01:31 -0700304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 if (S_ISDIR(inode->i_mode)) {
Al Viro94ee8492010-06-07 00:45:56 -0400306 DDPRINTK("ncp_evict_inode: put directory %ld\n", inode->i_ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 }
308
309 if (ncp_make_closed(inode) != 0) {
310 /* We can't do anything but complain. */
Joe Perchesb41f8b82014-04-08 16:04:14 -0700311 pr_err("%s: could not close\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313}
314
315static void ncp_stop_tasks(struct ncp_server *server) {
316 struct sock* sk = server->ncp_sock->sk;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200317
318 lock_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 sk->sk_error_report = server->error_report;
320 sk->sk_data_ready = server->data_ready;
321 sk->sk_write_space = server->write_space;
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200322 release_sock(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 del_timer_sync(&server->timeout_tm);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100324
Tejun Heo43829732012-08-20 14:51:24 -0700325 flush_work(&server->rcv.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100326 if (sk->sk_socket->type == SOCK_STREAM)
Tejun Heo43829732012-08-20 14:51:24 -0700327 flush_work(&server->tx.tq);
Tejun Heo5d8e4bd2010-12-24 15:59:06 +0100328 else
Tejun Heo43829732012-08-20 14:51:24 -0700329 flush_work(&server->timeout_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Al Viro34c80b12011-12-08 21:32:45 -0500332static int ncp_show_options(struct seq_file *seq, struct dentry *root)
Miklos Szeredi564cd132008-02-08 04:21:46 -0800333{
Al Viro34c80b12011-12-08 21:32:45 -0500334 struct ncp_server *server = NCP_SBP(root->d_sb);
Miklos Szeredi564cd132008-02-08 04:21:46 -0800335 unsigned int tmp;
336
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800337 if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
338 seq_printf(seq, ",uid=%u",
339 from_kuid_munged(&init_user_ns, server->m.uid));
340 if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
341 seq_printf(seq, ",gid=%u",
342 from_kgid_munged(&init_user_ns, server->m.gid));
343 if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
344 seq_printf(seq, ",owner=%u",
345 from_kuid_munged(&init_user_ns, server->m.mounted_uid));
Miklos Szeredi564cd132008-02-08 04:21:46 -0800346 tmp = server->m.file_mode & S_IALLUGO;
347 if (tmp != NCP_DEFAULT_FILE_MODE)
348 seq_printf(seq, ",mode=0%o", tmp);
349 tmp = server->m.dir_mode & S_IALLUGO;
350 if (tmp != NCP_DEFAULT_DIR_MODE)
351 seq_printf(seq, ",dirmode=0%o", tmp);
352 if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
353 tmp = server->m.time_out * 100 / HZ;
354 seq_printf(seq, ",timeout=%u", tmp);
355 }
356 if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
357 seq_printf(seq, ",retry=%u", server->m.retry_count);
358 if (server->m.flags != 0)
359 seq_printf(seq, ",flags=%lu", server->m.flags);
360 if (server->m.wdog_pid != NULL)
361 seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
362
363 return 0;
364}
365
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366static const struct ncp_option ncp_opts[] = {
367 { "uid", OPT_INT, 'u' },
368 { "gid", OPT_INT, 'g' },
369 { "owner", OPT_INT, 'o' },
370 { "mode", OPT_INT, 'm' },
371 { "dirmode", OPT_INT, 'd' },
372 { "timeout", OPT_INT, 't' },
373 { "retry", OPT_INT, 'r' },
374 { "flags", OPT_INT, 'f' },
375 { "wdogpid", OPT_INT, 'w' },
376 { "ncpfd", OPT_INT, 'n' },
377 { "infofd", OPT_INT, 'i' }, /* v5 */
378 { "version", OPT_INT, 'v' },
379 { NULL, 0, 0 } };
380
381static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
382 int optval;
383 char *optarg;
384 unsigned long optint;
385 int version = 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800386 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
388 data->flags = 0;
389 data->int_flags = 0;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800390 data->mounted_uid = GLOBAL_ROOT_UID;
Eric W. Biederman21542272006-12-13 00:35:11 -0800391 data->wdog_pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 data->ncp_fd = ~0;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800393 data->time_out = NCP_DEFAULT_TIME_OUT;
394 data->retry_count = NCP_DEFAULT_RETRY_COUNT;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800395 data->uid = GLOBAL_ROOT_UID;
396 data->gid = GLOBAL_ROOT_GID;
Miklos Szeredi564cd132008-02-08 04:21:46 -0800397 data->file_mode = NCP_DEFAULT_FILE_MODE;
398 data->dir_mode = NCP_DEFAULT_DIR_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 data->info_fd = -1;
400 data->mounted_vol[0] = 0;
401
402 while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
Eric W. Biederman1de24122006-12-13 00:35:13 -0800403 ret = optval;
404 if (ret < 0)
405 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 switch (optval) {
407 case 'u':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800408 data->uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800409 if (!uid_valid(data->uid)) {
410 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800411 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800412 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 break;
414 case 'g':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800415 data->gid = make_kgid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800416 if (!gid_valid(data->gid)) {
417 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800418 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 break;
421 case 'o':
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800422 data->mounted_uid = make_kuid(current_user_ns(), optint);
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800423 if (!uid_valid(data->mounted_uid)) {
424 ret = -EINVAL;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800425 goto err;
Wei Yongjun4fbeb192013-07-04 21:43:32 +0800426 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 break;
428 case 'm':
429 data->file_mode = optint;
430 break;
431 case 'd':
432 data->dir_mode = optint;
433 break;
434 case 't':
435 data->time_out = optint;
436 break;
437 case 'r':
438 data->retry_count = optint;
439 break;
440 case 'f':
441 data->flags = optint;
442 break;
443 case 'w':
Eric W. Biederman21542272006-12-13 00:35:11 -0800444 data->wdog_pid = find_get_pid(optint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 break;
446 case 'n':
447 data->ncp_fd = optint;
448 break;
449 case 'i':
450 data->info_fd = optint;
451 break;
452 case 'v':
Eric W. Biederman1de24122006-12-13 00:35:13 -0800453 ret = -ECHRNG;
454 if (optint < NCP_MOUNT_VERSION_V4)
455 goto err;
456 if (optint > NCP_MOUNT_VERSION_V5)
457 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 version = optint;
459 break;
460
461 }
462 }
463 return 0;
Eric W. Biederman1de24122006-12-13 00:35:13 -0800464err:
465 put_pid(data->wdog_pid);
466 data->wdog_pid = NULL;
467 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
470static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
471{
472 struct ncp_mount_data_kernel data;
473 struct ncp_server *server;
474 struct file *ncp_filp;
475 struct inode *root_inode;
476 struct inode *sock_inode;
477 struct socket *sock;
478 int error;
479 int default_bufsize;
480#ifdef CONFIG_NCPFS_PACKET_SIGNING
481 int options;
482#endif
483 struct ncp_entry_info finfo;
484
Andrew Morton2a5cac12011-05-24 17:13:42 -0700485 memset(&data, 0, sizeof(data));
Panagiotis Issarisf8314dc2006-09-27 01:49:37 -0700486 server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 if (!server)
488 return -ENOMEM;
489 sb->s_fs_info = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
491 error = -EFAULT;
492 if (raw_data == NULL)
493 goto out;
494 switch (*(int*)raw_data) {
495 case NCP_MOUNT_VERSION:
496 {
497 struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
498
499 data.flags = md->flags;
500 data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800501 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800502 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 data.ncp_fd = md->ncp_fd;
504 data.time_out = md->time_out;
505 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800506 data.uid = make_kuid(current_user_ns(), md->uid);
507 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 data.file_mode = md->file_mode;
509 data.dir_mode = md->dir_mode;
510 data.info_fd = -1;
511 memcpy(data.mounted_vol, md->mounted_vol,
512 NCP_VOLNAME_LEN+1);
513 }
514 break;
515 case NCP_MOUNT_VERSION_V4:
516 {
517 struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
518
519 data.flags = md->flags;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800520 data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
Eric W. Biederman21542272006-12-13 00:35:11 -0800521 data.wdog_pid = find_get_pid(md->wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 data.ncp_fd = md->ncp_fd;
523 data.time_out = md->time_out;
524 data.retry_count = md->retry_count;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800525 data.uid = make_kuid(current_user_ns(), md->uid);
526 data.gid = make_kgid(current_user_ns(), md->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 data.file_mode = md->file_mode;
528 data.dir_mode = md->dir_mode;
529 data.info_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 }
531 break;
532 default:
533 error = -ECHRNG;
534 if (memcmp(raw_data, "vers", 4) == 0) {
535 error = ncp_parse_options(&data, raw_data);
536 }
537 if (error)
538 goto out;
539 break;
540 }
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800541 error = -EINVAL;
542 if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
543 !gid_valid(data.gid))
544 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 error = -EBADF;
546 ncp_filp = fget(data.ncp_fd);
547 if (!ncp_filp)
548 goto out;
549 error = -ENOTSOCK;
Al Viro496ad9a2013-01-23 17:07:38 -0500550 sock_inode = file_inode(ncp_filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 if (!S_ISSOCK(sock_inode->i_mode))
552 goto out_fput;
553 sock = SOCKET_I(sock_inode);
554 if (!sock)
555 goto out_fput;
556
557 if (sock->type == SOCK_STREAM)
558 default_bufsize = 0xF000;
559 else
560 default_bufsize = 1024;
561
562 sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
563 sb->s_maxbytes = 0xFFFFFFFFU;
564 sb->s_blocksize = 1024; /* Eh... Is this correct? */
565 sb->s_blocksize_bits = 10;
566 sb->s_magic = NCP_SUPER_MAGIC;
567 sb->s_op = &ncp_sops;
Al Viro0378c402011-01-12 17:25:03 -0500568 sb->s_d_op = &ncp_dentry_operations;
Jens Axboef1970c72010-04-22 12:31:11 +0200569 sb->s_bdi = &server->bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
571 server = NCP_SBP(sb);
572 memset(server, 0, sizeof(*server));
573
Jens Axboef1970c72010-04-22 12:31:11 +0200574 error = bdi_setup_and_register(&server->bdi, "ncpfs", BDI_CAP_MAP_COPY);
575 if (error)
Djalal Harouni759c3612011-12-13 02:47:29 +0100576 goto out_fput;
Jens Axboef1970c72010-04-22 12:31:11 +0200577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 server->ncp_filp = ncp_filp;
579 server->ncp_sock = sock;
580
581 if (data.info_fd != -1) {
582 struct socket *info_sock;
583
584 error = -EBADF;
585 server->info_filp = fget(data.info_fd);
586 if (!server->info_filp)
Djalal Harouni759c3612011-12-13 02:47:29 +0100587 goto out_bdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 error = -ENOTSOCK;
Al Viro496ad9a2013-01-23 17:07:38 -0500589 sock_inode = file_inode(server->info_filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (!S_ISSOCK(sock_inode->i_mode))
591 goto out_fput2;
592 info_sock = SOCKET_I(sock_inode);
593 if (!info_sock)
594 goto out_fput2;
595 error = -EBADFD;
596 if (info_sock->type != SOCK_STREAM)
597 goto out_fput2;
598 server->info_sock = info_sock;
599 }
600
601/* server->lock = 0; */
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800602 mutex_init(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 server->packet = NULL;
604/* server->buffer_size = 0; */
605/* server->conn_status = 0; */
606/* server->root_dentry = NULL; */
607/* server->root_setuped = 0; */
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200608 mutex_init(&server->root_setup_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609#ifdef CONFIG_NCPFS_PACKET_SIGNING
610/* server->sign_wanted = 0; */
611/* server->sign_active = 0; */
612#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200613 init_rwsem(&server->auth_rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 server->auth.auth_type = NCP_AUTH_NONE;
615/* server->auth.object_name_len = 0; */
616/* server->auth.object_name = NULL; */
617/* server->auth.object_type = 0; */
618/* server->priv.len = 0; */
619/* server->priv.data = NULL; */
620
621 server->m = data;
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300622 /* Although anything producing this is buggy, it happens
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 now because of PATH_MAX changes.. */
624 if (server->m.time_out < 1) {
625 server->m.time_out = 10;
Joe Perchesb41f8b82014-04-08 16:04:14 -0700626 pr_info("You need to recompile your ncpfs utils..\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 }
628 server->m.time_out = server->m.time_out * HZ / 100;
629 server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
630 server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
631
632#ifdef CONFIG_NCPFS_NLS
633 /* load the default NLS charsets */
634 server->nls_vol = load_nls_default();
635 server->nls_io = load_nls_default();
636#endif /* CONFIG_NCPFS_NLS */
637
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200638 atomic_set(&server->dentry_ttl, 0); /* no caching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 INIT_LIST_HEAD(&server->tx.requests);
Ingo Molnar8e3f9042006-03-23 03:00:43 -0800641 mutex_init(&server->rcv.creq_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 server->tx.creq = NULL;
643 server->rcv.creq = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 init_timer(&server->timeout_tm);
646#undef NCP_PACKET_SIZE
647#define NCP_PACKET_SIZE 131072
648 error = -ENOMEM;
649 server->packet_size = NCP_PACKET_SIZE;
650 server->packet = vmalloc(NCP_PACKET_SIZE);
651 if (server->packet == NULL)
652 goto out_nls;
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100653 server->txbuf = vmalloc(NCP_PACKET_SIZE);
654 if (server->txbuf == NULL)
655 goto out_packet;
656 server->rxbuf = vmalloc(NCP_PACKET_SIZE);
657 if (server->rxbuf == NULL)
658 goto out_txbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200660 lock_sock(sock->sk);
661 server->data_ready = sock->sk->sk_data_ready;
662 server->write_space = sock->sk->sk_write_space;
663 server->error_report = sock->sk->sk_error_report;
664 sock->sk->sk_user_data = server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 sock->sk->sk_data_ready = ncp_tcp_data_ready;
666 sock->sk->sk_error_report = ncp_tcp_error_report;
667 if (sock->type == SOCK_STREAM) {
668 server->rcv.ptr = (unsigned char*)&server->rcv.buf;
669 server->rcv.len = 10;
670 server->rcv.state = 0;
David Howellsc4028952006-11-22 14:57:56 +0000671 INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
672 INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 sock->sk->sk_write_space = ncp_tcp_write_space;
674 } else {
David Howellsc4028952006-11-22 14:57:56 +0000675 INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
676 INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 server->timeout_tm.data = (unsigned long)server;
678 server->timeout_tm.function = ncpdgram_timeout_call;
679 }
Petr Vandrovec2a4df5d2010-09-29 14:39:11 +0200680 release_sock(sock->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
682 ncp_lock_server(server);
683 error = ncp_connect(server);
684 ncp_unlock_server(server);
685 if (error < 0)
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100686 goto out_rxbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
688
689 error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
690#ifdef CONFIG_NCPFS_PACKET_SIGNING
691 if (ncp_negotiate_size_and_options(server, default_bufsize,
692 NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
693 {
694 if (options != NCP_DEFAULT_OPTIONS)
695 {
696 if (ncp_negotiate_size_and_options(server,
697 default_bufsize,
698 options & 2,
699 &(server->buffer_size), &options) != 0)
700
701 {
702 goto out_disconnect;
703 }
704 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200705 ncp_lock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 if (options & 2)
707 server->sign_wanted = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200708 ncp_unlock_server(server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 }
710 else
711#endif /* CONFIG_NCPFS_PACKET_SIGNING */
712 if (ncp_negotiate_buffersize(server, default_bufsize,
713 &(server->buffer_size)) != 0)
714 goto out_disconnect;
715 DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
716
717 memset(&finfo, 0, sizeof(finfo));
718 finfo.i.attributes = aDIR;
719 finfo.i.dataStreamSize = 0; /* ignored */
720 finfo.i.dirEntNum = 0;
721 finfo.i.DosDirNum = 0;
722#ifdef CONFIG_NCPFS_SMALLDOS
723 finfo.i.NSCreator = NW_NS_DOS;
724#endif
725 finfo.volume = NCP_NUMBER_OF_VOLUMES;
726 /* set dates of mountpoint to Jan 1, 1986; 00:00 */
727 finfo.i.creationTime = finfo.i.modifyTime
728 = cpu_to_le16(0x0000);
729 finfo.i.creationDate = finfo.i.modifyDate
730 = finfo.i.lastAccessDate
731 = cpu_to_le16(0x0C21);
732 finfo.i.nameLen = 0;
733 finfo.i.entryName[0] = '\0';
734
735 finfo.opened = 0;
736 finfo.ino = 2; /* tradition */
737
738 server->name_space[finfo.volume] = NW_NS_DOS;
739
740 error = -ENOMEM;
741 root_inode = ncp_iget(sb, &finfo);
742 if (!root_inode)
743 goto out_disconnect;
744 DPRINTK("ncp_fill_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
Al Viro48fde702012-01-08 22:15:13 -0500745 sb->s_root = d_make_root(root_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 if (!sb->s_root)
Al Viro48fde702012-01-08 22:15:13 -0500747 goto out_disconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 return 0;
749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750out_disconnect:
751 ncp_lock_server(server);
752 ncp_disconnect(server);
753 ncp_unlock_server(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100754out_rxbuf:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 ncp_stop_tasks(server);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100756 vfree(server->rxbuf);
757out_txbuf:
758 vfree(server->txbuf);
759out_packet:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 vfree(server->packet);
761out_nls:
762#ifdef CONFIG_NCPFS_NLS
763 unload_nls(server->nls_io);
764 unload_nls(server->nls_vol);
765#endif
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200766 mutex_destroy(&server->rcv.creq_mutex);
767 mutex_destroy(&server->root_setup_lock);
768 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769out_fput2:
770 if (server->info_filp)
771 fput(server->info_filp);
Jens Axboef1970c72010-04-22 12:31:11 +0200772out_bdi:
Djalal Harouni759c3612011-12-13 02:47:29 +0100773 bdi_destroy(&server->bdi);
774out_fput:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
776 *
Matthew Whitworthe956b4b2010-06-27 14:34:30 +0100777 * The previously used put_filp(ncp_filp); was bogus, since
778 * it doesn't perform proper unlocking.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 */
780 fput(ncp_filp);
781out:
Eric W. Biederman1de24122006-12-13 00:35:13 -0800782 put_pid(data.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 sb->s_fs_info = NULL;
784 kfree(server);
785 return error;
786}
787
Al Viro1dcddd42013-10-03 13:22:44 -0400788static void delayed_free(struct rcu_head *p)
789{
790 struct ncp_server *server = container_of(p, struct ncp_server, rcu);
791#ifdef CONFIG_NCPFS_NLS
792 /* unload the NLS charsets */
793 unload_nls(server->nls_vol);
794 unload_nls(server->nls_io);
795#endif /* CONFIG_NCPFS_NLS */
796 kfree(server);
797}
798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799static void ncp_put_super(struct super_block *sb)
800{
801 struct ncp_server *server = NCP_SBP(sb);
802
803 ncp_lock_server(server);
804 ncp_disconnect(server);
805 ncp_unlock_server(server);
806
807 ncp_stop_tasks(server);
808
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200809 mutex_destroy(&server->rcv.creq_mutex);
810 mutex_destroy(&server->root_setup_lock);
811 mutex_destroy(&server->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 if (server->info_filp)
814 fput(server->info_filp);
815 fput(server->ncp_filp);
Eric W. Biederman21542272006-12-13 00:35:11 -0800816 kill_pid(server->m.wdog_pid, SIGTERM, 1);
817 put_pid(server->m.wdog_pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Jens Axboef1970c72010-04-22 12:31:11 +0200819 bdi_destroy(&server->bdi);
Pekka Enberg44db77f2006-01-14 13:21:12 -0800820 kfree(server->priv.data);
821 kfree(server->auth.object_name);
Pierre Ossmanc5f93cf2007-02-19 11:34:43 +0100822 vfree(server->rxbuf);
823 vfree(server->txbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 vfree(server->packet);
Al Viro1dcddd42013-10-03 13:22:44 -0400825 call_rcu(&server->rcu, delayed_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826}
827
David Howells726c3342006-06-23 02:02:58 -0700828static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
830 struct dentry* d;
831 struct inode* i;
832 struct ncp_inode_info* ni;
833 struct ncp_server* s;
834 struct ncp_volume_info vi;
David Howells726c3342006-06-23 02:02:58 -0700835 struct super_block *sb = dentry->d_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 int err;
837 __u8 dh;
838
839 d = sb->s_root;
840 if (!d) {
841 goto dflt;
842 }
843 i = d->d_inode;
844 if (!i) {
845 goto dflt;
846 }
847 ni = NCP_FINFO(i);
848 if (!ni) {
849 goto dflt;
850 }
851 s = NCP_SBP(sb);
852 if (!s) {
853 goto dflt;
854 }
855 if (!s->m.mounted_vol[0]) {
856 goto dflt;
857 }
858
859 err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
860 if (err) {
861 goto dflt;
862 }
863 err = ncp_get_directory_info(s, dh, &vi);
864 ncp_dirhandle_free(s, dh);
865 if (err) {
866 goto dflt;
867 }
868 buf->f_type = NCP_SUPER_MAGIC;
869 buf->f_bsize = vi.sectors_per_block * 512;
870 buf->f_blocks = vi.total_blocks;
871 buf->f_bfree = vi.free_blocks;
872 buf->f_bavail = vi.free_blocks;
873 buf->f_files = vi.total_dir_entries;
874 buf->f_ffree = vi.available_dir_entries;
875 buf->f_namelen = 12;
876 return 0;
877
878 /* We cannot say how much disk space is left on a mounted
879 NetWare Server, because free space is distributed over
880 volumes, and the current user might have disk quotas. So
881 free space is not that simple to determine. Our decision
882 here is to err conservatively. */
883
884dflt:;
885 buf->f_type = NCP_SUPER_MAGIC;
886 buf->f_bsize = NCP_BLOCK_SIZE;
887 buf->f_blocks = 0;
888 buf->f_bfree = 0;
889 buf->f_bavail = 0;
890 buf->f_namelen = 12;
891 return 0;
892}
893
894int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
895{
896 struct inode *inode = dentry->d_inode;
897 int result = 0;
898 __le32 info_mask;
899 struct nw_modify_dos_info info;
900 struct ncp_server *server;
901
902 result = -EIO;
903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 server = NCP_SERVER(inode);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200905 if (!server) /* How this could happen? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 goto out;
907
Al Viro338b2f52013-06-15 05:53:23 +0400908 result = -EPERM;
909 if (IS_DEADDIR(dentry->d_inode))
910 goto out;
911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 /* ageing the dentry to force validation */
913 ncp_age_dentry(server, dentry);
914
915 result = inode_change_ok(inode, attr);
916 if (result < 0)
917 goto out;
918
919 result = -EPERM;
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800920 if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 goto out;
922
Eric W. Biederman1ac7fd82012-02-07 16:28:28 -0800923 if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 goto out;
925
926 if (((attr->ia_valid & ATTR_MODE) &&
927 (attr->ia_mode &
928 ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
929 goto out;
930
931 info_mask = 0;
932 memset(&info, 0, sizeof(info));
933
934#if 1
935 if ((attr->ia_valid & ATTR_MODE) != 0)
936 {
937 umode_t newmode = attr->ia_mode;
938
939 info_mask |= DM_ATTRIBUTES;
940
941 if (S_ISDIR(inode->i_mode)) {
942 newmode &= server->m.dir_mode;
943 } else {
944#ifdef CONFIG_NCPFS_EXTRAS
945 if (server->m.flags & NCP_MOUNT_EXTRAS) {
946 /* any non-default execute bit set */
947 if (newmode & ~server->m.file_mode & S_IXUGO)
948 info.attributes |= aSHARED | aSYSTEM;
949 /* read for group/world and not in default file_mode */
950 else if (newmode & ~server->m.file_mode & S_IRUGO)
951 info.attributes |= aSHARED;
952 } else
953#endif
954 newmode &= server->m.file_mode;
955 }
956 if (newmode & S_IWUGO)
957 info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
958 else
959 info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
960
961#ifdef CONFIG_NCPFS_NFS_NS
962 if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
963 result = ncp_modify_nfs_info(server,
964 NCP_FINFO(inode)->volNumber,
965 NCP_FINFO(inode)->dirEntNum,
966 attr->ia_mode, 0);
967 if (result != 0)
968 goto out;
969 info.attributes &= ~(aSHARED | aSYSTEM);
970 {
971 /* mark partial success */
972 struct iattr tmpattr;
973
974 tmpattr.ia_valid = ATTR_MODE;
975 tmpattr.ia_mode = attr->ia_mode;
976
Christoph Hellwig10257742010-06-04 11:30:02 +0200977 setattr_copy(inode, &tmpattr);
978 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 }
980 }
981#endif
982 }
983#endif
984
985 /* Do SIZE before attributes, otherwise mtime together with size does not work...
986 */
987 if ((attr->ia_valid & ATTR_SIZE) != 0) {
988 int written;
989
990 DPRINTK("ncpfs: trying to change size to %ld\n",
991 attr->ia_size);
992
993 if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
994 result = -EACCES;
995 goto out;
996 }
997 ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
998 attr->ia_size, 0, "", &written);
999
1000 /* According to ndir, the changes only take effect after
1001 closing the file */
1002 ncp_inode_close(inode);
1003 result = ncp_make_closed(inode);
1004 if (result)
1005 goto out;
Christoph Hellwig10257742010-06-04 11:30:02 +02001006
1007 if (attr->ia_size != i_size_read(inode)) {
Marco Stornelli3e7a8062012-12-15 11:57:03 +01001008 truncate_setsize(inode, attr->ia_size);
Christoph Hellwig10257742010-06-04 11:30:02 +02001009 mark_inode_dirty(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 }
1011 }
1012 if ((attr->ia_valid & ATTR_CTIME) != 0) {
1013 info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
1014 ncp_date_unix2dos(attr->ia_ctime.tv_sec,
1015 &info.creationTime, &info.creationDate);
1016 }
1017 if ((attr->ia_valid & ATTR_MTIME) != 0) {
1018 info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
1019 ncp_date_unix2dos(attr->ia_mtime.tv_sec,
1020 &info.modifyTime, &info.modifyDate);
1021 }
1022 if ((attr->ia_valid & ATTR_ATIME) != 0) {
1023 __le16 dummy;
1024 info_mask |= (DM_LAST_ACCESS_DATE);
1025 ncp_date_unix2dos(attr->ia_atime.tv_sec,
1026 &dummy, &info.lastAccessDate);
1027 }
1028 if (info_mask != 0) {
1029 result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
1030 inode, info_mask, &info);
1031 if (result != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
1033 /* NetWare seems not to allow this. I
1034 do not know why. So, just tell the
1035 user everything went fine. This is
1036 a terrible hack, but I do not know
1037 how to do this correctly. */
1038 result = 0;
1039 } else
1040 goto out;
1041 }
1042#ifdef CONFIG_NCPFS_STRONG
1043 if ((!result) && (info_mask & DM_ATTRIBUTES))
1044 NCP_FINFO(inode)->nwattr = info.attributes;
1045#endif
1046 }
Christoph Hellwig10257742010-06-04 11:30:02 +02001047 if (result)
1048 goto out;
1049
1050 setattr_copy(inode, attr);
1051 mark_inode_dirty(inode);
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053out:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001054 if (result > 0)
1055 result = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 return result;
1057}
1058
Al Viro3c26ff62010-07-25 11:46:36 +04001059static struct dentry *ncp_mount(struct file_system_type *fs_type,
1060 int flags, const char *dev_name, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
Al Viro3c26ff62010-07-25 11:46:36 +04001062 return mount_nodev(fs_type, flags, data, ncp_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063}
1064
1065static struct file_system_type ncp_fs_type = {
1066 .owner = THIS_MODULE,
1067 .name = "ncpfs",
Al Viro3c26ff62010-07-25 11:46:36 +04001068 .mount = ncp_mount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 .kill_sb = kill_anon_super,
Miklos Szeredi564cd132008-02-08 04:21:46 -08001070 .fs_flags = FS_BINARY_MOUNTDATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071};
Eric W. Biederman7f78e032013-03-02 19:39:14 -08001072MODULE_ALIAS_FS("ncpfs");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
1074static int __init init_ncp_fs(void)
1075{
1076 int err;
Robert P. J. Day7c28cba2008-02-06 01:37:09 -08001077 DPRINTK("ncpfs: init_ncp_fs called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 err = init_inodecache();
1080 if (err)
1081 goto out1;
1082 err = register_filesystem(&ncp_fs_type);
1083 if (err)
1084 goto out;
1085 return 0;
1086out:
1087 destroy_inodecache();
1088out1:
1089 return err;
1090}
1091
1092static void __exit exit_ncp_fs(void)
1093{
Robert P. J. Day7c28cba2008-02-06 01:37:09 -08001094 DPRINTK("ncpfs: exit_ncp_fs called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 unregister_filesystem(&ncp_fs_type);
1096 destroy_inodecache();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097}
1098
1099module_init(init_ncp_fs)
1100module_exit(exit_ncp_fs)
1101MODULE_LICENSE("GPL");