]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - fs/ncpfs/dir.c
License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[mirror_ubuntu-bionic-kernel.git] / fs / ncpfs / dir.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
1da177e4
LT
2/*
3 * dir.c
4 *
5 * Copyright (C) 1995, 1996 by Volker Lendecke
6 * Modified for big endian by J.F. Chadima and David S. Miller
7 * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
8 * Modified 1998, 1999 Wolfram Pienkoss for NLS
9 * Modified 1999 Wolfram Pienkoss for directory caching
10 * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
11 *
12 */
13
1da177e4
LT
14
15#include <linux/time.h>
16#include <linux/errno.h>
17#include <linux/stat.h>
18#include <linux/kernel.h>
1da177e4
LT
19#include <linux/vmalloc.h>
20#include <linux/mm.h>
34286d66 21#include <linux/namei.h>
7c0f6ba6 22#include <linux/uaccess.h>
1da177e4 23#include <asm/byteorder.h>
1da177e4 24
32c419d9 25#include "ncp_fs.h"
1da177e4 26
76f582a8 27static void ncp_read_volume_list(struct file *, struct dir_context *,
1da177e4 28 struct ncp_cache_control *);
76f582a8 29static void ncp_do_readdir(struct file *, struct dir_context *,
1da177e4
LT
30 struct ncp_cache_control *);
31
76f582a8 32static int ncp_readdir(struct file *, struct dir_context *);
1da177e4 33
ebfc3b49 34static int ncp_create(struct inode *, struct dentry *, umode_t, bool);
00cd8dd3 35static struct dentry *ncp_lookup(struct inode *, struct dentry *, unsigned int);
1da177e4 36static int ncp_unlink(struct inode *, struct dentry *);
18bb1db3 37static int ncp_mkdir(struct inode *, struct dentry *, umode_t);
1da177e4
LT
38static int ncp_rmdir(struct inode *, struct dentry *);
39static int ncp_rename(struct inode *, struct dentry *,
1cd66c93 40 struct inode *, struct dentry *, unsigned int);
1da177e4 41static int ncp_mknod(struct inode * dir, struct dentry *dentry,
1a67aafb 42 umode_t mode, dev_t rdev);
1da177e4
LT
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
4b6f5d20 49const struct file_operations ncp_dir_operations =
1da177e4 50{
ca572727 51 .llseek = generic_file_llseek,
1da177e4 52 .read = generic_read_dir,
76f582a8 53 .iterate = ncp_readdir,
93d84b6d 54 .unlocked_ioctl = ncp_ioctl,
54f67f63
PV
55#ifdef CONFIG_COMPAT
56 .compat_ioctl = ncp_compat_ioctl,
57#endif
1da177e4
LT
58};
59
92e1d5be 60const struct inode_operations ncp_dir_inode_operations =
1da177e4
LT
61{
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,
2773bf00 69 .rename = ncp_rename,
1da177e4
LT
70 .setattr = ncp_notify_change,
71};
72
73/*
74 * Dentry operations routines
75 */
0b728e19 76static int ncp_lookup_validate(struct dentry *, unsigned int);
da53be12 77static int ncp_hash_dentry(const struct dentry *, struct qstr *);
6fa67e70 78static int ncp_compare_dentry(const struct dentry *,
621e155a 79 unsigned int, const char *, const struct qstr *);
fe15ce44 80static int ncp_delete_dentry(const struct dentry *);
5e993e25 81static void ncp_d_prune(struct dentry *dentry);
1da177e4 82
0378c405 83const struct dentry_operations ncp_dentry_operations =
1da177e4
LT
84{
85 .d_revalidate = ncp_lookup_validate,
86 .d_hash = ncp_hash_dentry,
87 .d_compare = ncp_compare_dentry,
88 .d_delete = ncp_delete_dentry,
5e993e25 89 .d_prune = ncp_d_prune,
1da177e4
LT
90};
91
2e54eb96
PV
92#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
93
94static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
95{
96#ifdef CONFIG_NCPFS_SMALLDOS
97 int ns = ncp_namespace(i);
98
99 if ((ns == NW_NS_DOS)
100#ifdef CONFIG_NCPFS_OS2_NS
101 || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
102#endif /* CONFIG_NCPFS_OS2_NS */
103 )
104 return 0;
105#endif /* CONFIG_NCPFS_SMALLDOS */
106 return 1;
107}
108
109#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS)
110
621e155a 111static inline int ncp_case_sensitive(const struct inode *i)
2e54eb96
PV
112{
113#ifdef CONFIG_NCPFS_NFS_NS
621e155a 114 return ncp_namespace(i) == NW_NS_NFS;
2e54eb96
PV
115#else
116 return 0;
117#endif /* CONFIG_NCPFS_NFS_NS */
118}
119
1da177e4
LT
120/*
121 * Note: leave the hash unchanged if the directory
122 * is case-sensitive.
da53be12
LT
123 *
124 * Accessing the parent inode can be racy under RCU pathwalking.
125 * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
126 * the callers will handle races.
1da177e4
LT
127 */
128static int
da53be12 129ncp_hash_dentry(const struct dentry *dentry, struct qstr *this)
1da177e4 130{
2b0143b5 131 struct inode *inode = d_inode_rcu(dentry);
da53be12
LT
132
133 if (!inode)
134 return 0;
135
b1e6a015 136 if (!ncp_case_sensitive(inode)) {
2e54eb96
PV
137 struct nls_table *t;
138 unsigned long hash;
139 int i;
1da177e4 140
9a232de4 141 t = NCP_IO_TABLE(dentry->d_sb);
8387ff25 142 hash = init_name_hash(dentry);
1da177e4
LT
143 for (i=0; i<this->len ; i++)
144 hash = partial_name_hash(ncp_tolower(t, this->name[i]),
145 hash);
146 this->hash = end_name_hash(hash);
147 }
148 return 0;
149}
150
da53be12
LT
151/*
152 * Accessing the parent inode can be racy under RCU pathwalking.
153 * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
154 * the callers will handle races.
155 */
1da177e4 156static int
6fa67e70 157ncp_compare_dentry(const struct dentry *dentry,
621e155a 158 unsigned int len, const char *str, const struct qstr *name)
1da177e4 159{
da53be12
LT
160 struct inode *pinode;
161
621e155a 162 if (len != name->len)
1da177e4
LT
163 return 1;
164
6fa67e70 165 pinode = d_inode_rcu(dentry->d_parent);
da53be12
LT
166 if (!pinode)
167 return 1;
168
621e155a
NP
169 if (ncp_case_sensitive(pinode))
170 return strncmp(str, name->name, len);
1da177e4 171
621e155a 172 return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
1da177e4
LT
173}
174
175/*
176 * This is the callback from dput() when d_count is going to 0.
177 * We use this to unhash dentries with bad inodes.
178 * Closing files can be safely postponed until iput() - it's done there anyway.
179 */
180static int
fe15ce44 181ncp_delete_dentry(const struct dentry * dentry)
1da177e4 182{
2b0143b5 183 struct inode *inode = d_inode(dentry);
1da177e4
LT
184
185 if (inode) {
186 if (is_bad_inode(inode))
187 return 1;
188 } else
189 {
190 /* N.B. Unhash negative dentries? */
191 }
192 return 0;
193}
194
195static inline int
196ncp_single_volume(struct ncp_server *server)
197{
198 return (server->m.mounted_vol[0] != '\0');
199}
200
201static inline int ncp_is_server_root(struct inode *inode)
202{
a7400222
AV
203 return !ncp_single_volume(NCP_SERVER(inode)) &&
204 is_root_inode(inode);
1da177e4
LT
205}
206
207
208/*
209 * This is the callback when the dcache has a lookup hit.
210 */
211
212
213#ifdef CONFIG_NCPFS_STRONG
214/* try to delete a readonly file (NW R bit set) */
215
216static int
217ncp_force_unlink(struct inode *dir, struct dentry* dentry)
218{
219 int res=0x9c,res2;
220 struct nw_modify_dos_info info;
221 __le32 old_nwattr;
222 struct inode *inode;
223
224 memset(&info, 0, sizeof(info));
225
226 /* remove the Read-Only flag on the NW server */
2b0143b5 227 inode = d_inode(dentry);
1da177e4
LT
228
229 old_nwattr = NCP_FINFO(inode)->nwattr;
230 info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
231 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
232 if (res2)
233 goto leave_me;
234
235 /* now try again the delete operation */
236 res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
237
238 if (res) /* delete failed, set R bit again */
239 {
240 info.attributes = old_nwattr;
241 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
242 if (res2)
243 goto leave_me;
244 }
245leave_me:
246 return(res);
247}
248#endif /* CONFIG_NCPFS_STRONG */
249
250#ifdef CONFIG_NCPFS_STRONG
251static int
252ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
253 struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
254{
255 struct nw_modify_dos_info info;
256 int res=0x90,res2;
2b0143b5 257 struct inode *old_inode = d_inode(old_dentry);
1da177e4
LT
258 __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
259 __le32 new_nwattr = 0; /* shut compiler warning */
260 int old_nwattr_changed = 0;
261 int new_nwattr_changed = 0;
262
263 memset(&info, 0, sizeof(info));
264
265 /* remove the Read-Only flag on the NW server */
266
267 info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
268 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
269 if (!res2)
270 old_nwattr_changed = 1;
2b0143b5
DH
271 if (new_dentry && d_really_is_positive(new_dentry)) {
272 new_nwattr = NCP_FINFO(d_inode(new_dentry))->nwattr;
1da177e4
LT
273 info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
274 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
275 if (!res2)
276 new_nwattr_changed = 1;
277 }
278 /* now try again the rename operation */
279 /* but only if something really happened */
280 if (new_nwattr_changed || old_nwattr_changed) {
281 res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
282 old_dir, _old_name,
283 new_dir, _new_name);
284 }
285 if (res)
286 goto leave_me;
287 /* file was successfully renamed, so:
288 do not set attributes on old file - it no longer exists
289 copy attributes from old file to new */
290 new_nwattr_changed = old_nwattr_changed;
291 new_nwattr = old_nwattr;
292 old_nwattr_changed = 0;
293
294leave_me:;
295 if (old_nwattr_changed) {
296 info.attributes = old_nwattr;
297 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
298 /* ignore errors */
299 }
300 if (new_nwattr_changed) {
301 info.attributes = new_nwattr;
302 res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
303 /* ignore errors */
304 }
305 return(res);
306}
307#endif /* CONFIG_NCPFS_STRONG */
308
309
310static int
0b728e19 311ncp_lookup_validate(struct dentry *dentry, unsigned int flags)
1da177e4
LT
312{
313 struct ncp_server *server;
314 struct dentry *parent;
315 struct inode *dir;
316 struct ncp_entry_info finfo;
317 int res, val = 0, len;
318 __u8 __name[NCP_MAXPATHLEN + 1];
319
0378c405
AV
320 if (dentry == dentry->d_sb->s_root)
321 return 1;
322
0b728e19 323 if (flags & LOOKUP_RCU)
34286d66
NP
324 return -ECHILD;
325
1da177e4 326 parent = dget_parent(dentry);
2b0143b5 327 dir = d_inode(parent);
1da177e4 328
2b0143b5 329 if (d_really_is_negative(dentry))
1da177e4
LT
330 goto finished;
331
332 server = NCP_SERVER(dir);
333
1da177e4
LT
334 /*
335 * Inspired by smbfs:
336 * The default validation is based on dentry age:
337 * We set the max age at mount time. (But each
338 * successful server lookup renews the timestamp.)
339 */
340 val = NCP_TEST_AGE(server, dentry);
341 if (val)
342 goto finished;
343
d3b73ca1 344 ncp_dbg(2, "%pd2 not valid, age=%ld, server lookup\n",
84eb3532 345 dentry, NCP_GET_AGE(dentry));
1da177e4
LT
346
347 len = sizeof(__name);
348 if (ncp_is_server_root(dir)) {
349 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
350 dentry->d_name.len, 1);
2e54eb96 351 if (!res) {
1da177e4 352 res = ncp_lookup_volume(server, __name, &(finfo.i));
2e54eb96
PV
353 if (!res)
354 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
355 }
1da177e4
LT
356 } else {
357 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
358 dentry->d_name.len, !ncp_preserve_case(dir));
359 if (!res)
360 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
361 }
362 finfo.volume = finfo.i.volNumber;
d3b73ca1 363 ncp_dbg(2, "looked for %pd/%s, res=%d\n",
84eb3532 364 dentry->d_parent, __name, res);
1da177e4
LT
365 /*
366 * If we didn't find it, or if it has a different dirEntNum to
367 * what we remember, it's not valid any more.
368 */
369 if (!res) {
2b0143b5 370 struct inode *inode = d_inode(dentry);
2e54eb96 371
5955102c 372 inode_lock(inode);
2e54eb96 373 if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) {
1da177e4
LT
374 ncp_new_dentry(dentry);
375 val=1;
376 } else
d3b73ca1 377 ncp_dbg(2, "found, but dirEntNum changed\n");
1da177e4 378
2e54eb96 379 ncp_update_inode2(inode, &finfo);
5955102c 380 inode_unlock(inode);
1da177e4
LT
381 }
382
383finished:
d3b73ca1 384 ncp_dbg(2, "result=%d\n", val);
1da177e4
LT
385 dput(parent);
386 return val;
387}
388
1da177e4
LT
389static time_t ncp_obtain_mtime(struct dentry *dentry)
390{
2b0143b5 391 struct inode *inode = d_inode(dentry);
1da177e4
LT
392 struct ncp_server *server = NCP_SERVER(inode);
393 struct nw_info_struct i;
394
395 if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
396 return 0;
397
398 if (ncp_obtain_info(server, inode, NULL, &i))
399 return 0;
400
401 return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
402}
403
5e993e25
AV
404static inline void
405ncp_invalidate_dircache_entries(struct dentry *parent)
406{
2b0143b5 407 struct ncp_server *server = NCP_SERVER(d_inode(parent));
5e993e25
AV
408 struct dentry *dentry;
409
410 spin_lock(&parent->d_lock);
411 list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
412 dentry->d_fsdata = NULL;
413 ncp_age_dentry(server, dentry);
414 }
415 spin_unlock(&parent->d_lock);
416}
417
76f582a8 418static int ncp_readdir(struct file *file, struct dir_context *ctx)
1da177e4 419{
76f582a8 420 struct dentry *dentry = file->f_path.dentry;
2b0143b5 421 struct inode *inode = d_inode(dentry);
1da177e4
LT
422 struct page *page = NULL;
423 struct ncp_server *server = NCP_SERVER(inode);
424 union ncp_dir_cache *cache = NULL;
425 struct ncp_cache_control ctl;
426 int result, mtime_valid = 0;
427 time_t mtime = 0;
428
1da177e4
LT
429 ctl.page = NULL;
430 ctl.cache = NULL;
431
d3b73ca1 432 ncp_dbg(2, "reading %pD2, pos=%d\n", file, (int)ctx->pos);
1da177e4
LT
433
434 result = -EIO;
2e54eb96 435 /* Do not generate '.' and '..' when server is dead. */
1da177e4
LT
436 if (!ncp_conn_valid(server))
437 goto out;
438
439 result = 0;
76f582a8
AV
440 if (!dir_emit_dots(file, ctx))
441 goto out;
1da177e4
LT
442
443 page = grab_cache_page(&inode->i_data, 0);
444 if (!page)
445 goto read_really;
446
447 ctl.cache = cache = kmap(page);
448 ctl.head = cache->head;
449
450 if (!PageUptodate(page) || !ctl.head.eof)
451 goto init_cache;
452
76f582a8 453 if (ctx->pos == 2) {
1da177e4
LT
454 if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
455 goto init_cache;
456
457 mtime = ncp_obtain_mtime(dentry);
458 mtime_valid = 1;
459 if ((!mtime) || (mtime != ctl.head.mtime))
460 goto init_cache;
461 }
462
76f582a8 463 if (ctx->pos > ctl.head.end)
1da177e4
LT
464 goto finished;
465
76f582a8 466 ctl.fpos = ctx->pos + (NCP_DIRCACHE_START - 2);
1da177e4
LT
467 ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
468 ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
469
470 for (;;) {
471 if (ctl.ofs != 0) {
472 ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
473 if (!ctl.page)
474 goto invalid_cache;
475 ctl.cache = kmap(ctl.page);
476 if (!PageUptodate(ctl.page))
477 goto invalid_cache;
478 }
479 while (ctl.idx < NCP_DIRCACHE_SIZE) {
480 struct dentry *dent;
76f582a8 481 bool over;
1da177e4 482
5e993e25
AV
483 spin_lock(&dentry->d_lock);
484 if (!(NCP_FINFO(inode)->flags & NCPI_DIR_CACHE)) {
485 spin_unlock(&dentry->d_lock);
486 goto invalid_cache;
487 }
488 dent = ctl.cache->dentry[ctl.idx];
489 if (unlikely(!lockref_get_not_dead(&dent->d_lockref))) {
490 spin_unlock(&dentry->d_lock);
491 goto invalid_cache;
492 }
493 spin_unlock(&dentry->d_lock);
2b0143b5 494 if (d_really_is_negative(dent)) {
5e993e25 495 dput(dent);
1da177e4 496 goto invalid_cache;
5e993e25 497 }
76f582a8
AV
498 over = !dir_emit(ctx, dent->d_name.name,
499 dent->d_name.len,
2b0143b5 500 d_inode(dent)->i_ino, DT_UNKNOWN);
1da177e4 501 dput(dent);
76f582a8 502 if (over)
1da177e4 503 goto finished;
76f582a8 504 ctx->pos += 1;
1da177e4 505 ctl.idx += 1;
76f582a8 506 if (ctx->pos > ctl.head.end)
1da177e4
LT
507 goto finished;
508 }
509 if (ctl.page) {
510 kunmap(ctl.page);
511 SetPageUptodate(ctl.page);
512 unlock_page(ctl.page);
09cbfeaf 513 put_page(ctl.page);
1da177e4
LT
514 ctl.page = NULL;
515 }
516 ctl.idx = 0;
517 ctl.ofs += 1;
518 }
519invalid_cache:
520 if (ctl.page) {
521 kunmap(ctl.page);
522 unlock_page(ctl.page);
09cbfeaf 523 put_page(ctl.page);
1da177e4
LT
524 ctl.page = NULL;
525 }
526 ctl.cache = cache;
527init_cache:
528 ncp_invalidate_dircache_entries(dentry);
529 if (!mtime_valid) {
530 mtime = ncp_obtain_mtime(dentry);
531 mtime_valid = 1;
532 }
533 ctl.head.mtime = mtime;
534 ctl.head.time = jiffies;
535 ctl.head.eof = 0;
536 ctl.fpos = 2;
537 ctl.ofs = 0;
538 ctl.idx = NCP_DIRCACHE_START;
539 ctl.filled = 0;
540 ctl.valid = 1;
541read_really:
5e993e25
AV
542 spin_lock(&dentry->d_lock);
543 NCP_FINFO(inode)->flags |= NCPI_DIR_CACHE;
544 spin_unlock(&dentry->d_lock);
1da177e4 545 if (ncp_is_server_root(inode)) {
76f582a8 546 ncp_read_volume_list(file, ctx, &ctl);
1da177e4 547 } else {
76f582a8 548 ncp_do_readdir(file, ctx, &ctl);
1da177e4
LT
549 }
550 ctl.head.end = ctl.fpos - 1;
551 ctl.head.eof = ctl.valid;
552finished:
2e54eb96
PV
553 if (ctl.page) {
554 kunmap(ctl.page);
555 SetPageUptodate(ctl.page);
556 unlock_page(ctl.page);
09cbfeaf 557 put_page(ctl.page);
2e54eb96 558 }
1da177e4
LT
559 if (page) {
560 cache->head = ctl.head;
561 kunmap(page);
562 SetPageUptodate(page);
563 unlock_page(page);
09cbfeaf 564 put_page(page);
1da177e4 565 }
1da177e4 566out:
1da177e4
LT
567 return result;
568}
569
5e993e25
AV
570static void ncp_d_prune(struct dentry *dentry)
571{
572 if (!dentry->d_fsdata) /* not referenced from page cache */
573 return;
2b0143b5 574 NCP_FINFO(d_inode(dentry->d_parent))->flags &= ~NCPI_DIR_CACHE;
5e993e25
AV
575}
576
1da177e4 577static int
76f582a8 578ncp_fill_cache(struct file *file, struct dir_context *ctx,
2e54eb96
PV
579 struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
580 int inval_childs)
1da177e4 581{
76f582a8 582 struct dentry *newdent, *dentry = file->f_path.dentry;
2b0143b5 583 struct inode *dir = d_inode(dentry);
1da177e4
LT
584 struct ncp_cache_control ctl = *ctrl;
585 struct qstr qname;
586 int valid = 0;
587 int hashed = 0;
588 ino_t ino = 0;
589 __u8 __name[NCP_MAXPATHLEN + 1];
590
591 qname.len = sizeof(__name);
2e54eb96 592 if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len,
1da177e4 593 entry->i.entryName, entry->i.nameLen,
2e54eb96 594 !ncp_preserve_entry_case(dir, entry->i.NSCreator)))
1da177e4
LT
595 return 1; /* I'm not sure */
596
597 qname.name = __name;
1da177e4 598
4f522a24 599 newdent = d_hash_and_lookup(dentry, &qname);
a1c83681 600 if (IS_ERR(newdent))
4f522a24 601 goto end_advance;
1da177e4
LT
602 if (!newdent) {
603 newdent = d_alloc(dentry, &qname);
604 if (!newdent)
605 goto end_advance;
606 } else {
607 hashed = 1;
2e54eb96
PV
608
609 /* If case sensitivity changed for this volume, all entries below this one
610 should be thrown away. This entry itself is not affected, as its case
611 sensitivity is controlled by its own parent. */
612 if (inval_childs)
613 shrink_dcache_parent(newdent);
614
615 /*
fb2d5b86
NP
616 * NetWare's OS2 namespace is case preserving yet case
617 * insensitive. So we update dentry's name as received from
618 * server. Parent dir's i_mutex is locked because we're in
619 * readdir.
2e54eb96 620 */
fb2d5b86 621 dentry_update_name_case(newdent, &qname);
1da177e4
LT
622 }
623
2b0143b5 624 if (d_really_is_negative(newdent)) {
2e54eb96
PV
625 struct inode *inode;
626
1da177e4 627 entry->opened = 0;
2e54eb96
PV
628 entry->ino = iunique(dir->i_sb, 2);
629 inode = ncp_iget(dir->i_sb, entry);
630 if (inode) {
2e54eb96 631 d_instantiate(newdent, inode);
1da177e4
LT
632 if (!hashed)
633 d_rehash(newdent);
5e993e25
AV
634 } else {
635 spin_lock(&dentry->d_lock);
803c0012 636 NCP_FINFO(dir)->flags &= ~NCPI_DIR_CACHE;
5e993e25 637 spin_unlock(&dentry->d_lock);
1da177e4 638 }
2e54eb96 639 } else {
2b0143b5 640 struct inode *inode = d_inode(newdent);
2e54eb96 641
5955102c 642 inode_lock_nested(inode, I_MUTEX_CHILD);
2e54eb96 643 ncp_update_inode2(inode, entry);
5955102c 644 inode_unlock(inode);
2e54eb96 645 }
1da177e4 646
1da177e4
LT
647 if (ctl.idx >= NCP_DIRCACHE_SIZE) {
648 if (ctl.page) {
649 kunmap(ctl.page);
650 SetPageUptodate(ctl.page);
651 unlock_page(ctl.page);
09cbfeaf 652 put_page(ctl.page);
1da177e4
LT
653 }
654 ctl.cache = NULL;
655 ctl.idx -= NCP_DIRCACHE_SIZE;
656 ctl.ofs += 1;
2e54eb96 657 ctl.page = grab_cache_page(&dir->i_data, ctl.ofs);
1da177e4
LT
658 if (ctl.page)
659 ctl.cache = kmap(ctl.page);
660 }
661 if (ctl.cache) {
2b0143b5 662 if (d_really_is_positive(newdent)) {
5e993e25
AV
663 newdent->d_fsdata = newdent;
664 ctl.cache->dentry[ctl.idx] = newdent;
2b0143b5 665 ino = d_inode(newdent)->i_ino;
5e993e25
AV
666 ncp_new_dentry(newdent);
667 }
668 valid = 1;
1da177e4
LT
669 }
670 dput(newdent);
671end_advance:
672 if (!valid)
673 ctl.valid = 0;
76f582a8 674 if (!ctl.filled && (ctl.fpos == ctx->pos)) {
1da177e4 675 if (!ino)
2e54eb96 676 ino = iunique(dir->i_sb, 2);
76f582a8
AV
677 ctl.filled = !dir_emit(ctx, qname.name, qname.len,
678 ino, DT_UNKNOWN);
1da177e4 679 if (!ctl.filled)
76f582a8 680 ctx->pos += 1;
1da177e4
LT
681 }
682 ctl.fpos += 1;
683 ctl.idx += 1;
684 *ctrl = ctl;
685 return (ctl.valid || !ctl.filled);
686}
687
688static void
76f582a8 689ncp_read_volume_list(struct file *file, struct dir_context *ctx,
1da177e4
LT
690 struct ncp_cache_control *ctl)
691{
a67f797d 692 struct inode *inode = file_inode(file);
1da177e4
LT
693 struct ncp_server *server = NCP_SERVER(inode);
694 struct ncp_volume_info info;
695 struct ncp_entry_info entry;
696 int i;
697
d3b73ca1 698 ncp_dbg(1, "pos=%ld\n", (unsigned long)ctx->pos);
1da177e4
LT
699
700 for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
2e54eb96 701 int inval_dentry;
1da177e4
LT
702
703 if (ncp_get_volume_info_with_number(server, i, &info) != 0)
704 return;
705 if (!strlen(info.volume_name))
706 continue;
707
d3b73ca1 708 ncp_dbg(1, "found vol: %s\n", info.volume_name);
1da177e4
LT
709
710 if (ncp_lookup_volume(server, info.volume_name,
711 &entry.i)) {
d3b73ca1 712 ncp_dbg(1, "could not lookup vol %s\n",
1da177e4
LT
713 info.volume_name);
714 continue;
715 }
2e54eb96 716 inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
1da177e4 717 entry.volume = entry.i.volNumber;
76f582a8 718 if (!ncp_fill_cache(file, ctx, ctl, &entry, inval_dentry))
1da177e4
LT
719 return;
720 }
721}
722
723static void
76f582a8 724ncp_do_readdir(struct file *file, struct dir_context *ctx,
1da177e4
LT
725 struct ncp_cache_control *ctl)
726{
a67f797d 727 struct inode *dir = file_inode(file);
1da177e4
LT
728 struct ncp_server *server = NCP_SERVER(dir);
729 struct nw_search_sequence seq;
730 struct ncp_entry_info entry;
731 int err;
732 void* buf;
733 int more;
734 size_t bufsize;
735
d3b73ca1 736 ncp_dbg(1, "%pD2, fpos=%ld\n", file, (unsigned long)ctx->pos);
e45ca8ba
JP
737 ncp_vdbg("init %pD, volnum=%d, dirent=%u\n",
738 file, NCP_FINFO(dir)->volNumber, NCP_FINFO(dir)->dirEntNum);
1da177e4
LT
739
740 err = ncp_initialize_search(server, dir, &seq);
741 if (err) {
d3b73ca1 742 ncp_dbg(1, "init failed, err=%d\n", err);
1da177e4
LT
743 return;
744 }
1da177e4
LT
745 /* We MUST NOT use server->buffer_size handshaked with server if we are
746 using UDP, as for UDP server uses max. buffer size determined by
747 MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
748 So we use 128KB, just to be sure, as there is no way how to know
749 this value in advance. */
750 bufsize = 131072;
751 buf = vmalloc(bufsize);
752 if (!buf)
753 return;
754 do {
755 int cnt;
756 char* rpl;
757 size_t rpls;
758
759 err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls);
760 if (err) /* Error */
761 break;
762 if (!cnt) /* prevent endless loop */
763 break;
764 while (cnt--) {
765 size_t onerpl;
766
767 if (rpls < offsetof(struct nw_info_struct, entryName))
768 break; /* short packet */
769 ncp_extract_file_info(rpl, &entry.i);
770 onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen;
771 if (rpls < onerpl)
772 break; /* short packet */
773 (void)ncp_obtain_nfs_info(server, &entry.i);
774 rpl += onerpl;
775 rpls -= onerpl;
776 entry.volume = entry.i.volNumber;
76f582a8 777 if (!ncp_fill_cache(file, ctx, ctl, &entry, 0))
1da177e4
LT
778 break;
779 }
780 } while (more);
781 vfree(buf);
1da177e4
LT
782 return;
783}
784
785int ncp_conn_logged_in(struct super_block *sb)
786{
787 struct ncp_server* server = NCP_SBP(sb);
788 int result;
789
790 if (ncp_single_volume(server)) {
791 int len;
792 struct dentry* dent;
793 __u32 volNumber;
794 __le32 dirEntNum;
795 __le32 DosDirNum;
796 __u8 __name[NCP_MAXPATHLEN + 1];
797
798 len = sizeof(__name);
799 result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
800 strlen(server->m.mounted_vol), 1);
801 if (result)
802 goto out;
803 result = -ENOENT;
804 if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) {
e45ca8ba 805 ncp_vdbg("%s not found\n", server->m.mounted_vol);
1da177e4
LT
806 goto out;
807 }
808 dent = sb->s_root;
809 if (dent) {
2b0143b5 810 struct inode* ino = d_inode(dent);
1da177e4 811 if (ino) {
2e54eb96 812 ncp_update_known_namespace(server, volNumber, NULL);
1da177e4
LT
813 NCP_FINFO(ino)->volNumber = volNumber;
814 NCP_FINFO(ino)->dirEntNum = dirEntNum;
815 NCP_FINFO(ino)->DosDirNum = DosDirNum;
2e54eb96 816 result = 0;
1da177e4 817 } else {
2b0143b5 818 ncp_dbg(1, "d_inode(sb->s_root) == NULL!\n");
1da177e4
LT
819 }
820 } else {
d3b73ca1 821 ncp_dbg(1, "sb->s_root == NULL!\n");
1da177e4 822 }
2e54eb96
PV
823 } else
824 result = 0;
1da177e4
LT
825
826out:
827 return result;
828}
829
00cd8dd3 830static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
1da177e4
LT
831{
832 struct ncp_server *server = NCP_SERVER(dir);
833 struct inode *inode = NULL;
834 struct ncp_entry_info finfo;
835 int error, res, len;
836 __u8 __name[NCP_MAXPATHLEN + 1];
837
1da177e4
LT
838 error = -EIO;
839 if (!ncp_conn_valid(server))
840 goto finished;
841
e45ca8ba 842 ncp_vdbg("server lookup for %pd2\n", dentry);
1da177e4
LT
843
844 len = sizeof(__name);
845 if (ncp_is_server_root(dir)) {
846 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
847 dentry->d_name.len, 1);
848 if (!res)
849 res = ncp_lookup_volume(server, __name, &(finfo.i));
ffddc5fd
DC
850 if (!res)
851 ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
1da177e4
LT
852 } else {
853 res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
854 dentry->d_name.len, !ncp_preserve_case(dir));
855 if (!res)
856 res = ncp_obtain_info(server, dir, __name, &(finfo.i));
857 }
e45ca8ba 858 ncp_vdbg("looked for %pd2, res=%d\n", dentry, res);
1da177e4
LT
859 /*
860 * If we didn't find an entry, make a negative dentry.
861 */
862 if (res)
863 goto add_entry;
864
865 /*
866 * Create an inode for the entry.
867 */
868 finfo.opened = 0;
869 finfo.ino = iunique(dir->i_sb, 2);
870 finfo.volume = finfo.i.volNumber;
871 error = -EACCES;
872 inode = ncp_iget(dir->i_sb, &finfo);
873
874 if (inode) {
875 ncp_new_dentry(dentry);
876add_entry:
1da177e4
LT
877 d_add(dentry, inode);
878 error = 0;
879 }
880
881finished:
e45ca8ba 882 ncp_vdbg("result=%d\n", error);
1da177e4
LT
883 return ERR_PTR(error);
884}
885
886/*
887 * This code is common to create, mkdir, and mknod.
888 */
889static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
890 struct ncp_entry_info *finfo)
891{
892 struct inode *inode;
893 int error = -EINVAL;
894
895 finfo->ino = iunique(dir->i_sb, 2);
896 inode = ncp_iget(dir->i_sb, finfo);
897 if (!inode)
898 goto out_close;
899 d_instantiate(dentry,inode);
900 error = 0;
901out:
902 return error;
903
904out_close:
e45ca8ba 905 ncp_vdbg("%pd2 failed, closing file\n", dentry);
1da177e4
LT
906 ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
907 goto out;
908}
909
5eee25ca 910int ncp_create_new(struct inode *dir, struct dentry *dentry, umode_t mode,
1da177e4
LT
911 dev_t rdev, __le32 attributes)
912{
913 struct ncp_server *server = NCP_SERVER(dir);
914 struct ncp_entry_info finfo;
915 int error, result, len;
916 int opmode;
917 __u8 __name[NCP_MAXPATHLEN + 1];
918
e45ca8ba 919 ncp_vdbg("creating %pd2, mode=%hx\n", dentry, mode);
1da177e4 920
1da177e4
LT
921 ncp_age_dentry(server, dentry);
922 len = sizeof(__name);
923 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
924 dentry->d_name.len, !ncp_preserve_case(dir));
925 if (error)
926 goto out;
927
928 error = -EACCES;
929
930 if (S_ISREG(mode) &&
931 (server->m.flags & NCP_MOUNT_EXTRAS) &&
932 (mode & S_IXUGO))
933 attributes |= aSYSTEM | aSHARED;
934
935 result = ncp_open_create_file_or_subdir(server, dir, __name,
936 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
937 attributes, AR_READ | AR_WRITE, &finfo);
938 opmode = O_RDWR;
939 if (result) {
940 result = ncp_open_create_file_or_subdir(server, dir, __name,
941 OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
942 attributes, AR_WRITE, &finfo);
943 if (result) {
944 if (result == 0x87)
945 error = -ENAMETOOLONG;
2e54eb96
PV
946 else if (result < 0)
947 error = result;
d3b73ca1 948 ncp_dbg(1, "%pd2 failed\n", dentry);
1da177e4
LT
949 goto out;
950 }
951 opmode = O_WRONLY;
952 }
953 finfo.access = opmode;
954 if (ncp_is_nfs_extras(server, finfo.volume)) {
955 finfo.i.nfs.mode = mode;
956 finfo.i.nfs.rdev = new_encode_dev(rdev);
957 if (ncp_modify_nfs_info(server, finfo.volume,
958 finfo.i.dirEntNum,
959 mode, new_encode_dev(rdev)) != 0)
960 goto out;
961 }
962
963 error = ncp_instantiate(dir, dentry, &finfo);
964out:
1da177e4
LT
965 return error;
966}
967
4acdaf27 968static int ncp_create(struct inode *dir, struct dentry *dentry, umode_t mode,
ebfc3b49 969 bool excl)
1da177e4
LT
970{
971 return ncp_create_new(dir, dentry, mode, 0, 0);
972}
973
18bb1db3 974static int ncp_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
1da177e4
LT
975{
976 struct ncp_entry_info finfo;
977 struct ncp_server *server = NCP_SERVER(dir);
978 int error, len;
979 __u8 __name[NCP_MAXPATHLEN + 1];
980
d3b73ca1 981 ncp_dbg(1, "making %pd2\n", dentry);
1da177e4 982
1da177e4
LT
983 ncp_age_dentry(server, dentry);
984 len = sizeof(__name);
985 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
986 dentry->d_name.len, !ncp_preserve_case(dir));
987 if (error)
988 goto out;
989
2e54eb96 990 error = ncp_open_create_file_or_subdir(server, dir, __name,
1da177e4
LT
991 OC_MODE_CREATE, aDIR,
992 cpu_to_le16(0xffff),
2e54eb96
PV
993 &finfo);
994 if (error == 0) {
1da177e4
LT
995 if (ncp_is_nfs_extras(server, finfo.volume)) {
996 mode |= S_IFDIR;
997 finfo.i.nfs.mode = mode;
998 if (ncp_modify_nfs_info(server,
999 finfo.volume,
1000 finfo.i.dirEntNum,
1001 mode, 0) != 0)
1002 goto out;
1003 }
1004 error = ncp_instantiate(dir, dentry, &finfo);
2e54eb96
PV
1005 } else if (error > 0) {
1006 error = -EACCES;
1da177e4
LT
1007 }
1008out:
1da177e4
LT
1009 return error;
1010}
1011
1012static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
1013{
1014 struct ncp_server *server = NCP_SERVER(dir);
1015 int error, result, len;
1016 __u8 __name[NCP_MAXPATHLEN + 1];
1017
d3b73ca1 1018 ncp_dbg(1, "removing %pd2\n", dentry);
1da177e4 1019
1da177e4
LT
1020 len = sizeof(__name);
1021 error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
1022 dentry->d_name.len, !ncp_preserve_case(dir));
1023 if (error)
1024 goto out;
1025
1026 result = ncp_del_file_or_subdir(server, dir, __name);
1027 switch (result) {
1028 case 0x00:
1029 error = 0;
1030 break;
1031 case 0x85: /* unauthorized to delete file */
1032 case 0x8A: /* unauthorized to delete file */
1033 error = -EACCES;
1034 break;
1035 case 0x8F:
1036 case 0x90: /* read only */
1037 error = -EPERM;
1038 break;
1039 case 0x9F: /* in use by another client */
1040 error = -EBUSY;
1041 break;
1042 case 0xA0: /* directory not empty */
1043 error = -ENOTEMPTY;
1044 break;
1045 case 0xFF: /* someone deleted file */
1046 error = -ENOENT;
1047 break;
1048 default:
2e54eb96 1049 error = result < 0 ? result : -EACCES;
1da177e4
LT
1050 break;
1051 }
1052out:
1da177e4
LT
1053 return error;
1054}
1055
1056static int ncp_unlink(struct inode *dir, struct dentry *dentry)
1057{
2b0143b5 1058 struct inode *inode = d_inode(dentry);
1da177e4
LT
1059 struct ncp_server *server;
1060 int error;
1061
1da177e4 1062 server = NCP_SERVER(dir);
d3b73ca1 1063 ncp_dbg(1, "unlinking %pd2\n", dentry);
1da177e4 1064
1da177e4
LT
1065 /*
1066 * Check whether to close the file ...
1067 */
1068 if (inode) {
e45ca8ba 1069 ncp_vdbg("closing file\n");
1da177e4
LT
1070 ncp_make_closed(inode);
1071 }
1072
1073 error = ncp_del_file_or_subdir2(server, dentry);
1074#ifdef CONFIG_NCPFS_STRONG
1075 /* 9C is Invalid path.. It should be 8F, 90 - read only, but
1076 it is not :-( */
1077 if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
1078 error = ncp_force_unlink(dir, dentry);
1079 }
1080#endif
1081 switch (error) {
1082 case 0x00:
d3b73ca1 1083 ncp_dbg(1, "removed %pd2\n", dentry);
1da177e4
LT
1084 break;
1085 case 0x85:
1086 case 0x8A:
1087 error = -EACCES;
1088 break;
1089 case 0x8D: /* some files in use */
1090 case 0x8E: /* all files in use */
1091 error = -EBUSY;
1092 break;
1093 case 0x8F: /* some read only */
1094 case 0x90: /* all read only */
1095 case 0x9C: /* !!! returned when in-use or read-only by NW4 */
1096 error = -EPERM;
1097 break;
1098 case 0xFF:
1099 error = -ENOENT;
1100 break;
1101 default:
2e54eb96 1102 error = error < 0 ? error : -EACCES;
1da177e4
LT
1103 break;
1104 }
1da177e4
LT
1105 return error;
1106}
1107
1108static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
1cd66c93
MS
1109 struct inode *new_dir, struct dentry *new_dentry,
1110 unsigned int flags)
1da177e4
LT
1111{
1112 struct ncp_server *server = NCP_SERVER(old_dir);
1113 int error;
1114 int old_len, new_len;
1115 __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
1116
1cd66c93
MS
1117 if (flags)
1118 return -EINVAL;
1119
d3b73ca1 1120 ncp_dbg(1, "%pd2 to %pd2\n", old_dentry, new_dentry);
1da177e4 1121
1da177e4
LT
1122 ncp_age_dentry(server, old_dentry);
1123 ncp_age_dentry(server, new_dentry);
1124
1125 old_len = sizeof(__old_name);
1126 error = ncp_io2vol(server, __old_name, &old_len,
1127 old_dentry->d_name.name, old_dentry->d_name.len,
1128 !ncp_preserve_case(old_dir));
1129 if (error)
1130 goto out;
1131
1132 new_len = sizeof(__new_name);
1133 error = ncp_io2vol(server, __new_name, &new_len,
1134 new_dentry->d_name.name, new_dentry->d_name.len,
1135 !ncp_preserve_case(new_dir));
1136 if (error)
1137 goto out;
1138
1139 error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
1140 new_dir, __new_name);
1141#ifdef CONFIG_NCPFS_STRONG
1142 if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
1143 server->m.flags & NCP_MOUNT_STRONG) { /* RO */
1144 error = ncp_force_rename(old_dir, old_dentry, __old_name,
1145 new_dir, new_dentry, __new_name);
1146 }
1147#endif
1148 switch (error) {
1149 case 0x00:
d3b73ca1
JP
1150 ncp_dbg(1, "renamed %pd -> %pd\n",
1151 old_dentry, new_dentry);
3f4a9494
AV
1152 ncp_d_prune(old_dentry);
1153 ncp_d_prune(new_dentry);
1da177e4
LT
1154 break;
1155 case 0x9E:
1156 error = -ENAMETOOLONG;
1157 break;
1158 case 0xFF:
1159 error = -ENOENT;
1160 break;
1161 default:
2e54eb96 1162 error = error < 0 ? error : -EACCES;
1da177e4
LT
1163 break;
1164 }
1165out:
1da177e4
LT
1166 return error;
1167}
1168
1169static int ncp_mknod(struct inode * dir, struct dentry *dentry,
1a67aafb 1170 umode_t mode, dev_t rdev)
1da177e4 1171{
1da177e4 1172 if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
d3b73ca1 1173 ncp_dbg(1, "mode = 0%ho\n", mode);
1da177e4
LT
1174 return ncp_create_new(dir, dentry, mode, rdev, 0);
1175 }
1176 return -EPERM; /* Strange, but true */
1177}
1178
1179/* The following routines are taken directly from msdos-fs */
1180
1181/* Linear day numbers of the respective 1sts in non-leap years. */
1182
1183static int day_n[] =
1184{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
1185/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
1186
1da177e4
LT
1187static int utc2local(int time)
1188{
1189 return time - sys_tz.tz_minuteswest * 60;
1190}
1191
1192static int local2utc(int time)
1193{
1194 return time + sys_tz.tz_minuteswest * 60;
1195}
1196
1197/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
1198int
1199ncp_date_dos2unix(__le16 t, __le16 d)
1200{
1201 unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d);
1202 int month, year, secs;
1203
1204 /* first subtract and mask after that... Otherwise, if
1205 date == 0, bad things happen */
1206 month = ((date >> 5) - 1) & 15;
1207 year = date >> 9;
1208 secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
1209 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
1210 year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
1211 /* days since 1.1.70 plus 80's leap day */
1212 return local2utc(secs);
1213}
1214
1215
1216/* Convert linear UNIX date to a MS-DOS time/date pair. */
1217void
1218ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
1219{
1220 int day, year, nl_day, month;
1221
1222 unix_date = utc2local(unix_date);
1223 *time = cpu_to_le16(
1224 (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
1225 (((unix_date / 3600) % 24) << 11));
1226 day = unix_date / 86400 - 3652;
1227 year = day / 365;
1228 if ((year + 3) / 4 + 365 * year > day)
1229 year--;
1230 day -= (year + 3) / 4 + 365 * year;
1231 if (day == 59 && !(year & 3)) {
1232 nl_day = day;
1233 month = 2;
1234 } else {
1235 nl_day = (year & 3) || day <= 59 ? day : day - 1;
c5df5913 1236 for (month = 1; month < 12; month++)
1da177e4
LT
1237 if (day_n[month] > nl_day)
1238 break;
1239 }
1240 *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
1241}