blob: 3bcc68aed41687ee2a374db58cf1ba4bc1277805 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * dir.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, 1999 Wolfram Pienkoss for NLS
8 * Modified 1999 Wolfram Pienkoss for directory caching
9 * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
10 *
11 */
12
Linus Torvalds1da177e2005-04-16 15:20:36 -070013
14#include <linux/time.h>
15#include <linux/errno.h>
16#include <linux/stat.h>
17#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/vmalloc.h>
19#include <linux/mm.h>
20#include <asm/uaccess.h>
21#include <asm/byteorder.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#include <linux/ncp_fs.h>
24
25#include "ncplib_kernel.h"
26
27static void ncp_read_volume_list(struct file *, void *, filldir_t,
28 struct ncp_cache_control *);
29static void ncp_do_readdir(struct file *, void *, filldir_t,
30 struct ncp_cache_control *);
31
32static int ncp_readdir(struct file *, void *, filldir_t);
33
34static int ncp_create(struct inode *, struct dentry *, int, struct nameidata *);
35static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *);
36static int ncp_unlink(struct inode *, struct dentry *);
37static int ncp_mkdir(struct inode *, struct dentry *, int);
38static int ncp_rmdir(struct inode *, struct dentry *);
39static int ncp_rename(struct inode *, struct dentry *,
40 struct inode *, struct dentry *);
41static int ncp_mknod(struct inode * dir, struct dentry *dentry,
42 int mode, dev_t rdev);
43#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
44extern int ncp_symlink(struct inode *, struct dentry *, const char *);
45#else
46#define ncp_symlink NULL
47#endif
48
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -080049const struct file_operations ncp_dir_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
jan Blunckca572722010-05-26 14:44:53 -070051 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 .read = generic_read_dir,
53 .readdir = ncp_readdir,
John Kacur93d84b62010-05-05 15:15:37 +020054 .unlocked_ioctl = ncp_ioctl,
Petr Vandrovec54f67f62006-09-30 23:27:55 -070055#ifdef CONFIG_COMPAT
56 .compat_ioctl = ncp_compat_ioctl,
57#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070058};
59
Arjan van de Ven92e1d5b2007-02-12 00:55:39 -080060const struct inode_operations ncp_dir_inode_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070061{
62 .create = ncp_create,
63 .lookup = ncp_lookup,
64 .unlink = ncp_unlink,
65 .symlink = ncp_symlink,
66 .mkdir = ncp_mkdir,
67 .rmdir = ncp_rmdir,
68 .mknod = ncp_mknod,
69 .rename = ncp_rename,
70 .setattr = ncp_notify_change,
71};
72
73/*
74 * Dentry operations routines
75 */
76static int ncp_lookup_validate(struct dentry *, struct nameidata *);
77static int ncp_hash_dentry(struct dentry *, struct qstr *);
Nick Piggin621e1552011-01-07 17:49:27 +110078static int ncp_compare_dentry(const struct dentry *, const struct inode *,
79 const struct dentry *, const struct inode *,
80 unsigned int, const char *, const struct qstr *);
Nick Pigginfe15ce42011-01-07 17:49:23 +110081static int ncp_delete_dentry(const struct dentry *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
Al Viroe16404e2009-02-20 05:55:13 +000083static const struct dentry_operations ncp_dentry_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070084{
85 .d_revalidate = ncp_lookup_validate,
86 .d_hash = ncp_hash_dentry,
87 .d_compare = ncp_compare_dentry,
88 .d_delete = ncp_delete_dentry,
89};
90
Al Viroe16404e2009-02-20 05:55:13 +000091const struct dentry_operations ncp_root_dentry_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
93 .d_hash = ncp_hash_dentry,
94 .d_compare = ncp_compare_dentry,
95 .d_delete = ncp_delete_dentry,
96};
97
98
Petr Vandrovec2e54eb92010-09-27 01:47:33 +020099#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
100
101static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
102{
103#ifdef CONFIG_NCPFS_SMALLDOS
104 int ns = ncp_namespace(i);
105
106 if ((ns == NW_NS_DOS)
107#ifdef CONFIG_NCPFS_OS2_NS
108 || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
109#endif /* CONFIG_NCPFS_OS2_NS */
110 )
111 return 0;
112#endif /* CONFIG_NCPFS_SMALLDOS */
113 return 1;
114}
115
116#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS)
117
Nick Piggin621e1552011-01-07 17:49:27 +1100118static inline int ncp_case_sensitive(const struct inode *i)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200119{
120#ifdef CONFIG_NCPFS_NFS_NS
Nick Piggin621e1552011-01-07 17:49:27 +1100121 return ncp_namespace(i) == NW_NS_NFS;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200122#else
123 return 0;
124#endif /* CONFIG_NCPFS_NFS_NS */
125}
126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127/*
128 * Note: leave the hash unchanged if the directory
129 * is case-sensitive.
130 */
131static int
132ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
133{
Nick Piggin621e1552011-01-07 17:49:27 +1100134 if (!ncp_case_sensitive(dentry->d_inode)) {
135 struct super_block *sb = dentry->d_sb;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200136 struct nls_table *t;
137 unsigned long hash;
138 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
Nick Piggin621e1552011-01-07 17:49:27 +1100140 t = NCP_IO_TABLE(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 hash = init_name_hash();
142 for (i=0; i<this->len ; i++)
143 hash = partial_name_hash(ncp_tolower(t, this->name[i]),
144 hash);
145 this->hash = end_name_hash(hash);
146 }
147 return 0;
148}
149
150static int
Nick Piggin621e1552011-01-07 17:49:27 +1100151ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
152 const struct dentry *dentry, const struct inode *inode,
153 unsigned int len, const char *str, const struct qstr *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154{
Nick Piggin621e1552011-01-07 17:49:27 +1100155 if (len != name->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 return 1;
157
Nick Piggin621e1552011-01-07 17:49:27 +1100158 if (ncp_case_sensitive(pinode))
159 return strncmp(str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Nick Piggin621e1552011-01-07 17:49:27 +1100161 return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
164/*
165 * This is the callback from dput() when d_count is going to 0.
166 * We use this to unhash dentries with bad inodes.
167 * Closing files can be safely postponed until iput() - it's done there anyway.
168 */
169static int
Nick Pigginfe15ce42011-01-07 17:49:23 +1100170ncp_delete_dentry(const struct dentry * dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
172 struct inode *inode = dentry->d_inode;
173
174 if (inode) {
175 if (is_bad_inode(inode))
176 return 1;
177 } else
178 {
179 /* N.B. Unhash negative dentries? */
180 }
181 return 0;
182}
183
184static inline int
185ncp_single_volume(struct ncp_server *server)
186{
187 return (server->m.mounted_vol[0] != '\0');
188}
189
190static inline int ncp_is_server_root(struct inode *inode)
191{
192 return (!ncp_single_volume(NCP_SERVER(inode)) &&
193 inode == inode->i_sb->s_root->d_inode);
194}
195
196
197/*
198 * This is the callback when the dcache has a lookup hit.
199 */
200
201
202#ifdef CONFIG_NCPFS_STRONG
203/* try to delete a readonly file (NW R bit set) */
204
205static int
206ncp_force_unlink(struct inode *dir, struct dentry* dentry)
207{
208 int res=0x9c,res2;
209 struct nw_modify_dos_info info;
210 __le32 old_nwattr;
211 struct inode *inode;
212
213 memset(&info, 0, sizeof(info));
214
215 /* remove the Read-Only flag on the NW server */
216 inode = dentry->d_inode;
217
218 old_nwattr = NCP_FINFO(inode)->nwattr;
219 info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
220 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
221 if (res2)
222 goto leave_me;
223
224 /* now try again the delete operation */
225 res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
226
227 if (res) /* delete failed, set R bit again */
228 {
229 info.attributes = old_nwattr;
230 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
231 if (res2)
232 goto leave_me;
233 }
234leave_me:
235 return(res);
236}
237#endif /* CONFIG_NCPFS_STRONG */
238
239#ifdef CONFIG_NCPFS_STRONG
240static int
241ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
242 struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
243{
244 struct nw_modify_dos_info info;
245 int res=0x90,res2;
246 struct inode *old_inode = old_dentry->d_inode;
247 __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
248 __le32 new_nwattr = 0; /* shut compiler warning */
249 int old_nwattr_changed = 0;
250 int new_nwattr_changed = 0;
251
252 memset(&info, 0, sizeof(info));
253
254 /* remove the Read-Only flag on the NW server */
255
256 info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
257 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
258 if (!res2)
259 old_nwattr_changed = 1;
260 if (new_dentry && new_dentry->d_inode) {
261 new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr;
262 info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
263 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
264 if (!res2)
265 new_nwattr_changed = 1;
266 }
267 /* now try again the rename operation */
268 /* but only if something really happened */
269 if (new_nwattr_changed || old_nwattr_changed) {
270 res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
271 old_dir, _old_name,
272 new_dir, _new_name);
273 }
274 if (res)
275 goto leave_me;
276 /* file was successfully renamed, so:
277 do not set attributes on old file - it no longer exists
278 copy attributes from old file to new */
279 new_nwattr_changed = old_nwattr_changed;
280 new_nwattr = old_nwattr;
281 old_nwattr_changed = 0;
282
283leave_me:;
284 if (old_nwattr_changed) {
285 info.attributes = old_nwattr;
286 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
287 /* ignore errors */
288 }
289 if (new_nwattr_changed) {
290 info.attributes = new_nwattr;
291 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
292 /* ignore errors */
293 }
294 return(res);
295}
296#endif /* CONFIG_NCPFS_STRONG */
297
298
299static int
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200300ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301{
302 struct ncp_server *server;
303 struct dentry *parent;
304 struct inode *dir;
305 struct ncp_entry_info finfo;
306 int res, val = 0, len;
307 __u8 __name[NCP_MAXPATHLEN + 1];
308
309 parent = dget_parent(dentry);
310 dir = parent->d_inode;
311
312 if (!dentry->d_inode)
313 goto finished;
314
315 server = NCP_SERVER(dir);
316
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 /*
318 * Inspired by smbfs:
319 * The default validation is based on dentry age:
320 * We set the max age at mount time. (But each
321 * successful server lookup renews the timestamp.)
322 */
323 val = NCP_TEST_AGE(server, dentry);
324 if (val)
325 goto finished;
326
327 DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n",
328 dentry->d_parent->d_name.name, dentry->d_name.name,
329 NCP_GET_AGE(dentry));
330
331 len = sizeof(__name);
332 if (ncp_is_server_root(dir)) {
333 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
334 dentry->d_name.len, 1);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200335 if (!res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200337 if (!res)
338 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
339 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 } else {
341 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
342 dentry->d_name.len, !ncp_preserve_case(dir));
343 if (!res)
344 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
345 }
346 finfo.volume = finfo.i.volNumber;
347 DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n",
348 dentry->d_parent->d_name.name, __name, res);
349 /*
350 * If we didn't find it, or if it has a different dirEntNum to
351 * what we remember, it's not valid any more.
352 */
353 if (!res) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200354 struct inode *inode = dentry->d_inode;
355
356 mutex_lock(&inode->i_mutex);
357 if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 ncp_new_dentry(dentry);
359 val=1;
360 } else
361 DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n");
362
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200363 ncp_update_inode2(inode, &finfo);
364 mutex_unlock(&inode->i_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366
367finished:
368 DDPRINTK("ncp_lookup_validate: result=%d\n", val);
369 dput(parent);
370 return val;
371}
372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373static struct dentry *
374ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
375{
376 struct dentry *dent = dentry;
377 struct list_head *next;
378
379 if (d_validate(dent, parent)) {
380 if (dent->d_name.len <= NCP_MAXPATHLEN &&
381 (unsigned long)dent->d_fsdata == fpos) {
382 if (!dent->d_inode) {
383 dput(dent);
384 dent = NULL;
385 }
386 return dent;
387 }
388 dput(dent);
389 }
390
391 /* If a pointer is invalid, we search the dentry. */
392 spin_lock(&dcache_lock);
393 next = parent->d_subdirs.next;
394 while (next != &parent->d_subdirs) {
Eric Dumazet5160ee62006-01-08 01:03:32 -0800395 dent = list_entry(next, struct dentry, d_u.d_child);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 if ((unsigned long)dent->d_fsdata == fpos) {
397 if (dent->d_inode)
398 dget_locked(dent);
399 else
400 dent = NULL;
401 spin_unlock(&dcache_lock);
402 goto out;
403 }
404 next = next->next;
405 }
406 spin_unlock(&dcache_lock);
407 return NULL;
408
409out:
410 return dent;
411}
412
413static time_t ncp_obtain_mtime(struct dentry *dentry)
414{
415 struct inode *inode = dentry->d_inode;
416 struct ncp_server *server = NCP_SERVER(inode);
417 struct nw_info_struct i;
418
419 if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
420 return 0;
421
422 if (ncp_obtain_info(server, inode, NULL, &i))
423 return 0;
424
425 return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
426}
427
428static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
429{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800430 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 struct inode *inode = dentry->d_inode;
432 struct page *page = NULL;
433 struct ncp_server *server = NCP_SERVER(inode);
434 union ncp_dir_cache *cache = NULL;
435 struct ncp_cache_control ctl;
436 int result, mtime_valid = 0;
437 time_t mtime = 0;
438
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 ctl.page = NULL;
440 ctl.cache = NULL;
441
442 DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
443 dentry->d_parent->d_name.name, dentry->d_name.name,
444 (int) filp->f_pos);
445
446 result = -EIO;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200447 /* Do not generate '.' and '..' when server is dead. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (!ncp_conn_valid(server))
449 goto out;
450
451 result = 0;
452 if (filp->f_pos == 0) {
453 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
454 goto out;
455 filp->f_pos = 1;
456 }
457 if (filp->f_pos == 1) {
458 if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR))
459 goto out;
460 filp->f_pos = 2;
461 }
462
463 page = grab_cache_page(&inode->i_data, 0);
464 if (!page)
465 goto read_really;
466
467 ctl.cache = cache = kmap(page);
468 ctl.head = cache->head;
469
470 if (!PageUptodate(page) || !ctl.head.eof)
471 goto init_cache;
472
473 if (filp->f_pos == 2) {
474 if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
475 goto init_cache;
476
477 mtime = ncp_obtain_mtime(dentry);
478 mtime_valid = 1;
479 if ((!mtime) || (mtime != ctl.head.mtime))
480 goto init_cache;
481 }
482
483 if (filp->f_pos > ctl.head.end)
484 goto finished;
485
486 ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
487 ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
488 ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
489
490 for (;;) {
491 if (ctl.ofs != 0) {
492 ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
493 if (!ctl.page)
494 goto invalid_cache;
495 ctl.cache = kmap(ctl.page);
496 if (!PageUptodate(ctl.page))
497 goto invalid_cache;
498 }
499 while (ctl.idx < NCP_DIRCACHE_SIZE) {
500 struct dentry *dent;
501 int res;
502
503 dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
504 dentry, filp->f_pos);
505 if (!dent)
506 goto invalid_cache;
507 res = filldir(dirent, dent->d_name.name,
508 dent->d_name.len, filp->f_pos,
509 dent->d_inode->i_ino, DT_UNKNOWN);
510 dput(dent);
511 if (res)
512 goto finished;
513 filp->f_pos += 1;
514 ctl.idx += 1;
515 if (filp->f_pos > ctl.head.end)
516 goto finished;
517 }
518 if (ctl.page) {
519 kunmap(ctl.page);
520 SetPageUptodate(ctl.page);
521 unlock_page(ctl.page);
522 page_cache_release(ctl.page);
523 ctl.page = NULL;
524 }
525 ctl.idx = 0;
526 ctl.ofs += 1;
527 }
528invalid_cache:
529 if (ctl.page) {
530 kunmap(ctl.page);
531 unlock_page(ctl.page);
532 page_cache_release(ctl.page);
533 ctl.page = NULL;
534 }
535 ctl.cache = cache;
536init_cache:
537 ncp_invalidate_dircache_entries(dentry);
538 if (!mtime_valid) {
539 mtime = ncp_obtain_mtime(dentry);
540 mtime_valid = 1;
541 }
542 ctl.head.mtime = mtime;
543 ctl.head.time = jiffies;
544 ctl.head.eof = 0;
545 ctl.fpos = 2;
546 ctl.ofs = 0;
547 ctl.idx = NCP_DIRCACHE_START;
548 ctl.filled = 0;
549 ctl.valid = 1;
550read_really:
551 if (ncp_is_server_root(inode)) {
552 ncp_read_volume_list(filp, dirent, filldir, &ctl);
553 } else {
554 ncp_do_readdir(filp, dirent, filldir, &ctl);
555 }
556 ctl.head.end = ctl.fpos - 1;
557 ctl.head.eof = ctl.valid;
558finished:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200559 if (ctl.page) {
560 kunmap(ctl.page);
561 SetPageUptodate(ctl.page);
562 unlock_page(ctl.page);
563 page_cache_release(ctl.page);
564 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 if (page) {
566 cache->head = ctl.head;
567 kunmap(page);
568 SetPageUptodate(page);
569 unlock_page(page);
570 page_cache_release(page);
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 return result;
574}
575
576static int
577ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200578 struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
579 int inval_childs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800581 struct dentry *newdent, *dentry = filp->f_path.dentry;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200582 struct inode *dir = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 struct ncp_cache_control ctl = *ctrl;
584 struct qstr qname;
585 int valid = 0;
586 int hashed = 0;
587 ino_t ino = 0;
588 __u8 __name[NCP_MAXPATHLEN + 1];
589
590 qname.len = sizeof(__name);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200591 if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 entry->i.entryName, entry->i.nameLen,
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200593 !ncp_preserve_entry_case(dir, entry->i.NSCreator)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 return 1; /* I'm not sure */
595
596 qname.name = __name;
597 qname.hash = full_name_hash(qname.name, qname.len);
598
599 if (dentry->d_op && dentry->d_op->d_hash)
600 if (dentry->d_op->d_hash(dentry, &qname) != 0)
601 goto end_advance;
602
603 newdent = d_lookup(dentry, &qname);
604
605 if (!newdent) {
606 newdent = d_alloc(dentry, &qname);
607 if (!newdent)
608 goto end_advance;
609 } else {
610 hashed = 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200611
612 /* If case sensitivity changed for this volume, all entries below this one
613 should be thrown away. This entry itself is not affected, as its case
614 sensitivity is controlled by its own parent. */
615 if (inval_childs)
616 shrink_dcache_parent(newdent);
617
618 /*
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100619 * NetWare's OS2 namespace is case preserving yet case
620 * insensitive. So we update dentry's name as received from
621 * server. Parent dir's i_mutex is locked because we're in
622 * readdir.
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200623 */
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100624 dentry_update_name_case(newdent, &qname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
626
627 if (!newdent->d_inode) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200628 struct inode *inode;
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 entry->opened = 0;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200631 entry->ino = iunique(dir->i_sb, 2);
632 inode = ncp_iget(dir->i_sb, entry);
633 if (inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 newdent->d_op = &ncp_dentry_operations;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200635 d_instantiate(newdent, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if (!hashed)
637 d_rehash(newdent);
638 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200639 } else {
640 struct inode *inode = newdent->d_inode;
641
Nick Pigginfb2d5b82011-01-07 17:49:26 +1100642 mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200643 ncp_update_inode2(inode, entry);
644 mutex_unlock(&inode->i_mutex);
645 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 if (newdent->d_inode) {
648 ino = newdent->d_inode->i_ino;
649 newdent->d_fsdata = (void *) ctl.fpos;
650 ncp_new_dentry(newdent);
651 }
652
653 if (ctl.idx >= NCP_DIRCACHE_SIZE) {
654 if (ctl.page) {
655 kunmap(ctl.page);
656 SetPageUptodate(ctl.page);
657 unlock_page(ctl.page);
658 page_cache_release(ctl.page);
659 }
660 ctl.cache = NULL;
661 ctl.idx -= NCP_DIRCACHE_SIZE;
662 ctl.ofs += 1;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200663 ctl.page = grab_cache_page(&dir->i_data, ctl.ofs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (ctl.page)
665 ctl.cache = kmap(ctl.page);
666 }
667 if (ctl.cache) {
668 ctl.cache->dentry[ctl.idx] = newdent;
669 valid = 1;
670 }
671 dput(newdent);
672end_advance:
673 if (!valid)
674 ctl.valid = 0;
675 if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
676 if (!ino)
677 ino = find_inode_number(dentry, &qname);
678 if (!ino)
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200679 ino = iunique(dir->i_sb, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 ctl.filled = filldir(dirent, qname.name, qname.len,
681 filp->f_pos, ino, DT_UNKNOWN);
682 if (!ctl.filled)
683 filp->f_pos += 1;
684 }
685 ctl.fpos += 1;
686 ctl.idx += 1;
687 *ctrl = ctl;
688 return (ctl.valid || !ctl.filled);
689}
690
691static void
692ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
693 struct ncp_cache_control *ctl)
694{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800695 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 struct inode *inode = dentry->d_inode;
697 struct ncp_server *server = NCP_SERVER(inode);
698 struct ncp_volume_info info;
699 struct ncp_entry_info entry;
700 int i;
701
702 DPRINTK("ncp_read_volume_list: pos=%ld\n",
703 (unsigned long) filp->f_pos);
704
705 for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200706 int inval_dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
708 if (ncp_get_volume_info_with_number(server, i, &info) != 0)
709 return;
710 if (!strlen(info.volume_name))
711 continue;
712
713 DPRINTK("ncp_read_volume_list: found vol: %s\n",
714 info.volume_name);
715
716 if (ncp_lookup_volume(server, info.volume_name,
717 &entry.i)) {
718 DPRINTK("ncpfs: could not lookup vol %s\n",
719 info.volume_name);
720 continue;
721 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200722 inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200724 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return;
726 }
727}
728
729static void
730ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
731 struct ncp_cache_control *ctl)
732{
Josef Sipek92e5bae2006-12-08 02:37:22 -0800733 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 struct inode *dir = dentry->d_inode;
735 struct ncp_server *server = NCP_SERVER(dir);
736 struct nw_search_sequence seq;
737 struct ncp_entry_info entry;
738 int err;
739 void* buf;
740 int more;
741 size_t bufsize;
742
743 DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
744 dentry->d_parent->d_name.name, dentry->d_name.name,
745 (unsigned long) filp->f_pos);
746 PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
747 dentry->d_name.name, NCP_FINFO(dir)->volNumber,
748 NCP_FINFO(dir)->dirEntNum);
749
750 err = ncp_initialize_search(server, dir, &seq);
751 if (err) {
752 DPRINTK("ncp_do_readdir: init failed, err=%d\n", err);
753 return;
754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 /* We MUST NOT use server->buffer_size handshaked with server if we are
756 using UDP, as for UDP server uses max. buffer size determined by
757 MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
758 So we use 128KB, just to be sure, as there is no way how to know
759 this value in advance. */
760 bufsize = 131072;
761 buf = vmalloc(bufsize);
762 if (!buf)
763 return;
764 do {
765 int cnt;
766 char* rpl;
767 size_t rpls;
768
769 err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls);
770 if (err) /* Error */
771 break;
772 if (!cnt) /* prevent endless loop */
773 break;
774 while (cnt--) {
775 size_t onerpl;
776
777 if (rpls < offsetof(struct nw_info_struct, entryName))
778 break; /* short packet */
779 ncp_extract_file_info(rpl, &entry.i);
780 onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen;
781 if (rpls < onerpl)
782 break; /* short packet */
783 (void)ncp_obtain_nfs_info(server, &entry.i);
784 rpl += onerpl;
785 rpls -= onerpl;
786 entry.volume = entry.i.volNumber;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200787 if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 break;
789 }
790 } while (more);
791 vfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 return;
793}
794
795int ncp_conn_logged_in(struct super_block *sb)
796{
797 struct ncp_server* server = NCP_SBP(sb);
798 int result;
799
800 if (ncp_single_volume(server)) {
801 int len;
802 struct dentry* dent;
803 __u32 volNumber;
804 __le32 dirEntNum;
805 __le32 DosDirNum;
806 __u8 __name[NCP_MAXPATHLEN + 1];
807
808 len = sizeof(__name);
809 result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
810 strlen(server->m.mounted_vol), 1);
811 if (result)
812 goto out;
813 result = -ENOENT;
814 if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) {
815 PPRINTK("ncp_conn_logged_in: %s not found\n",
816 server->m.mounted_vol);
817 goto out;
818 }
819 dent = sb->s_root;
820 if (dent) {
821 struct inode* ino = dent->d_inode;
822 if (ino) {
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200823 ncp_update_known_namespace(server, volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 NCP_FINFO(ino)->volNumber = volNumber;
825 NCP_FINFO(ino)->dirEntNum = dirEntNum;
826 NCP_FINFO(ino)->DosDirNum = DosDirNum;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200827 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 } else {
829 DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n");
830 }
831 } else {
832 DPRINTK("ncpfs: sb->s_root == NULL!\n");
833 }
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200834 } else
835 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837out:
838 return result;
839}
840
841static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
842{
843 struct ncp_server *server = NCP_SERVER(dir);
844 struct inode *inode = NULL;
845 struct ncp_entry_info finfo;
846 int error, res, len;
847 __u8 __name[NCP_MAXPATHLEN + 1];
848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 error = -EIO;
850 if (!ncp_conn_valid(server))
851 goto finished;
852
853 PPRINTK("ncp_lookup: server lookup for %s/%s\n",
854 dentry->d_parent->d_name.name, dentry->d_name.name);
855
856 len = sizeof(__name);
857 if (ncp_is_server_root(dir)) {
858 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
859 dentry->d_name.len, 1);
860 if (!res)
861 res = ncp_lookup_volume(server, __name, &(finfo.i));
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200862 if (!res)
863 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 } else {
865 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
866 dentry->d_name.len, !ncp_preserve_case(dir));
867 if (!res)
868 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
869 }
870 PPRINTK("ncp_lookup: looked for %s/%s, res=%d\n",
871 dentry->d_parent->d_name.name, __name, res);
872 /*
873 * If we didn't find an entry, make a negative dentry.
874 */
875 if (res)
876 goto add_entry;
877
878 /*
879 * Create an inode for the entry.
880 */
881 finfo.opened = 0;
882 finfo.ino = iunique(dir->i_sb, 2);
883 finfo.volume = finfo.i.volNumber;
884 error = -EACCES;
885 inode = ncp_iget(dir->i_sb, &finfo);
886
887 if (inode) {
888 ncp_new_dentry(dentry);
889add_entry:
890 dentry->d_op = &ncp_dentry_operations;
891 d_add(dentry, inode);
892 error = 0;
893 }
894
895finished:
896 PPRINTK("ncp_lookup: result=%d\n", error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 return ERR_PTR(error);
898}
899
900/*
901 * This code is common to create, mkdir, and mknod.
902 */
903static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
904 struct ncp_entry_info *finfo)
905{
906 struct inode *inode;
907 int error = -EINVAL;
908
909 finfo->ino = iunique(dir->i_sb, 2);
910 inode = ncp_iget(dir->i_sb, finfo);
911 if (!inode)
912 goto out_close;
913 d_instantiate(dentry,inode);
914 error = 0;
915out:
916 return error;
917
918out_close:
919 PPRINTK("ncp_instantiate: %s/%s failed, closing file\n",
920 dentry->d_parent->d_name.name, dentry->d_name.name);
921 ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
922 goto out;
923}
924
925int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
926 dev_t rdev, __le32 attributes)
927{
928 struct ncp_server *server = NCP_SERVER(dir);
929 struct ncp_entry_info finfo;
930 int error, result, len;
931 int opmode;
932 __u8 __name[NCP_MAXPATHLEN + 1];
933
934 PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n",
935 dentry->d_parent->d_name.name, dentry->d_name.name, mode);
936
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 ncp_age_dentry(server, dentry);
938 len = sizeof(__name);
939 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
940 dentry->d_name.len, !ncp_preserve_case(dir));
941 if (error)
942 goto out;
943
944 error = -EACCES;
945
946 if (S_ISREG(mode) &&
947 (server->m.flags & NCP_MOUNT_EXTRAS) &&
948 (mode & S_IXUGO))
949 attributes |= aSYSTEM | aSHARED;
950
951 result = ncp_open_create_file_or_subdir(server, dir, __name,
952 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
953 attributes, AR_READ | AR_WRITE, &finfo);
954 opmode = O_RDWR;
955 if (result) {
956 result = ncp_open_create_file_or_subdir(server, dir, __name,
957 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
958 attributes, AR_WRITE, &finfo);
959 if (result) {
960 if (result == 0x87)
961 error = -ENAMETOOLONG;
Petr Vandrovec2e54eb92010-09-27 01:47:33 +0200962 else if (result < 0)
963 error = result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 DPRINTK("ncp_create: %s/%s failed\n",
965 dentry->d_parent->d_name.name, dentry->d_name.name);
966 goto out;
967 }
968 opmode = O_WRONLY;
969 }
970 finfo.access = opmode;
971 if (ncp_is_nfs_extras(server, finfo.volume)) {
972 finfo.i.nfs.mode = mode;
973 finfo.i.nfs.rdev = new_encode_dev(rdev);
974 if (ncp_modify_nfs_info(server, finfo.volume,
975 finfo.i.dirEntNum,
976 mode, new_encode_dev(rdev)) != 0)
977 goto out;
978 }
979
980 error = ncp_instantiate(dir, dentry, &finfo);
981out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 return error;
983}
984
985static int ncp_create(struct inode *dir, struct dentry *dentry, int mode,
986 struct nameidata *nd)
987{
988 return ncp_create_new(dir, dentry, mode, 0, 0);
989}
990
991static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
992{
993 struct ncp_entry_info finfo;
994 struct ncp_server *server = NCP_SERVER(dir);
995 int error, len;
996 __u8 __name[NCP_MAXPATHLEN + 1];
997
998 DPRINTK("ncp_mkdir: making %s/%s\n",
999 dentry->d_parent->d_name.name, dentry->d_name.name);
1000
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 ncp_age_dentry(server, dentry);
1002 len = sizeof(__name);
1003 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1004 dentry->d_name.len, !ncp_preserve_case(dir));
1005 if (error)
1006 goto out;
1007
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001008 error = ncp_open_create_file_or_subdir(server, dir, __name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 OC_MODE_CREATE, aDIR,
1010 cpu_to_le16(0xffff),
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001011 &finfo);
1012 if (error == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 if (ncp_is_nfs_extras(server, finfo.volume)) {
1014 mode |= S_IFDIR;
1015 finfo.i.nfs.mode = mode;
1016 if (ncp_modify_nfs_info(server,
1017 finfo.volume,
1018 finfo.i.dirEntNum,
1019 mode, 0) != 0)
1020 goto out;
1021 }
1022 error = ncp_instantiate(dir, dentry, &finfo);
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001023 } else if (error > 0) {
1024 error = -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 }
1026out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 return error;
1028}
1029
1030static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
1031{
1032 struct ncp_server *server = NCP_SERVER(dir);
1033 int error, result, len;
1034 __u8 __name[NCP_MAXPATHLEN + 1];
1035
1036 DPRINTK("ncp_rmdir: removing %s/%s\n",
1037 dentry->d_parent->d_name.name, dentry->d_name.name);
1038
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 error = -EBUSY;
1040 if (!d_unhashed(dentry))
1041 goto out;
1042
1043 len = sizeof(__name);
1044 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1045 dentry->d_name.len, !ncp_preserve_case(dir));
1046 if (error)
1047 goto out;
1048
1049 result = ncp_del_file_or_subdir(server, dir, __name);
1050 switch (result) {
1051 case 0x00:
1052 error = 0;
1053 break;
1054 case 0x85: /* unauthorized to delete file */
1055 case 0x8A: /* unauthorized to delete file */
1056 error = -EACCES;
1057 break;
1058 case 0x8F:
1059 case 0x90: /* read only */
1060 error = -EPERM;
1061 break;
1062 case 0x9F: /* in use by another client */
1063 error = -EBUSY;
1064 break;
1065 case 0xA0: /* directory not empty */
1066 error = -ENOTEMPTY;
1067 break;
1068 case 0xFF: /* someone deleted file */
1069 error = -ENOENT;
1070 break;
1071 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001072 error = result < 0 ? result : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 break;
1074 }
1075out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return error;
1077}
1078
1079static int ncp_unlink(struct inode *dir, struct dentry *dentry)
1080{
1081 struct inode *inode = dentry->d_inode;
1082 struct ncp_server *server;
1083 int error;
1084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 server = NCP_SERVER(dir);
1086 DPRINTK("ncp_unlink: unlinking %s/%s\n",
1087 dentry->d_parent->d_name.name, dentry->d_name.name);
1088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 /*
1090 * Check whether to close the file ...
1091 */
1092 if (inode) {
1093 PPRINTK("ncp_unlink: closing file\n");
1094 ncp_make_closed(inode);
1095 }
1096
1097 error = ncp_del_file_or_subdir2(server, dentry);
1098#ifdef CONFIG_NCPFS_STRONG
1099 /* 9C is Invalid path.. It should be 8F, 90 - read only, but
1100 it is not :-( */
1101 if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
1102 error = ncp_force_unlink(dir, dentry);
1103 }
1104#endif
1105 switch (error) {
1106 case 0x00:
1107 DPRINTK("ncp: removed %s/%s\n",
1108 dentry->d_parent->d_name.name, dentry->d_name.name);
1109 break;
1110 case 0x85:
1111 case 0x8A:
1112 error = -EACCES;
1113 break;
1114 case 0x8D: /* some files in use */
1115 case 0x8E: /* all files in use */
1116 error = -EBUSY;
1117 break;
1118 case 0x8F: /* some read only */
1119 case 0x90: /* all read only */
1120 case 0x9C: /* !!! returned when in-use or read-only by NW4 */
1121 error = -EPERM;
1122 break;
1123 case 0xFF:
1124 error = -ENOENT;
1125 break;
1126 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001127 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 break;
1129 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 return error;
1131}
1132
1133static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
1134 struct inode *new_dir, struct dentry *new_dentry)
1135{
1136 struct ncp_server *server = NCP_SERVER(old_dir);
1137 int error;
1138 int old_len, new_len;
1139 __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
1140
1141 DPRINTK("ncp_rename: %s/%s to %s/%s\n",
1142 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
1143 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
1144
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 ncp_age_dentry(server, old_dentry);
1146 ncp_age_dentry(server, new_dentry);
1147
1148 old_len = sizeof(__old_name);
1149 error = ncp_io2vol(server, __old_name, &old_len,
1150 old_dentry->d_name.name, old_dentry->d_name.len,
1151 !ncp_preserve_case(old_dir));
1152 if (error)
1153 goto out;
1154
1155 new_len = sizeof(__new_name);
1156 error = ncp_io2vol(server, __new_name, &new_len,
1157 new_dentry->d_name.name, new_dentry->d_name.len,
1158 !ncp_preserve_case(new_dir));
1159 if (error)
1160 goto out;
1161
1162 error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
1163 new_dir, __new_name);
1164#ifdef CONFIG_NCPFS_STRONG
1165 if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
1166 server->m.flags & NCP_MOUNT_STRONG) { /* RO */
1167 error = ncp_force_rename(old_dir, old_dentry, __old_name,
1168 new_dir, new_dentry, __new_name);
1169 }
1170#endif
1171 switch (error) {
1172 case 0x00:
1173 DPRINTK("ncp renamed %s -> %s.\n",
1174 old_dentry->d_name.name,new_dentry->d_name.name);
1175 break;
1176 case 0x9E:
1177 error = -ENAMETOOLONG;
1178 break;
1179 case 0xFF:
1180 error = -ENOENT;
1181 break;
1182 default:
Petr Vandrovec2e54eb92010-09-27 01:47:33 +02001183 error = error < 0 ? error : -EACCES;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 break;
1185 }
1186out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 return error;
1188}
1189
1190static int ncp_mknod(struct inode * dir, struct dentry *dentry,
1191 int mode, dev_t rdev)
1192{
1193 if (!new_valid_dev(rdev))
1194 return -EINVAL;
1195 if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
1196 DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode);
1197 return ncp_create_new(dir, dentry, mode, rdev, 0);
1198 }
1199 return -EPERM; /* Strange, but true */
1200}
1201
1202/* The following routines are taken directly from msdos-fs */
1203
1204/* Linear day numbers of the respective 1sts in non-leap years. */
1205
1206static int day_n[] =
1207{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
1208/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
1209
1210
1211extern struct timezone sys_tz;
1212
1213static int utc2local(int time)
1214{
1215 return time - sys_tz.tz_minuteswest * 60;
1216}
1217
1218static int local2utc(int time)
1219{
1220 return time + sys_tz.tz_minuteswest * 60;
1221}
1222
1223/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
1224int
1225ncp_date_dos2unix(__le16 t, __le16 d)
1226{
1227 unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d);
1228 int month, year, secs;
1229
1230 /* first subtract and mask after that... Otherwise, if
1231 date == 0, bad things happen */
1232 month = ((date >> 5) - 1) & 15;
1233 year = date >> 9;
1234 secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
1235 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
1236 year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
1237 /* days since 1.1.70 plus 80's leap day */
1238 return local2utc(secs);
1239}
1240
1241
1242/* Convert linear UNIX date to a MS-DOS time/date pair. */
1243void
1244ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
1245{
1246 int day, year, nl_day, month;
1247
1248 unix_date = utc2local(unix_date);
1249 *time = cpu_to_le16(
1250 (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
1251 (((unix_date / 3600) % 24) << 11));
1252 day = unix_date / 86400 - 3652;
1253 year = day / 365;
1254 if ((year + 3) / 4 + 365 * year > day)
1255 year--;
1256 day -= (year + 3) / 4 + 365 * year;
1257 if (day == 59 && !(year & 3)) {
1258 nl_day = day;
1259 month = 2;
1260 } else {
1261 nl_day = (year & 3) || day <= 59 ? day : day - 1;
Roel Kluinc5df5912009-09-22 16:45:54 -07001262 for (month = 1; month < 12; month++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 if (day_n[month] > nl_day)
1264 break;
1265 }
1266 *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
1267}