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