Import changeset
[linux-flexiantxendom0-3.2.10.git] / fs / ncpfs / ncplib_kernel.c
1 /*
2  *  ncplib_kernel.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 1999 Wolfram Pienkoss for NLS
8  *
9  */
10
11
12 #include <linux/config.h>
13
14 #include "ncplib_kernel.h"
15
16 static inline int min(int a, int b)
17 {
18         return a < b ? a : b;
19 }
20
21 static inline void assert_server_locked(struct ncp_server *server)
22 {
23         if (server->lock == 0) {
24                 DPRINTK("ncpfs: server not locked!\n");
25         }
26 }
27
28 static void ncp_add_byte(struct ncp_server *server, __u8 x)
29 {
30         assert_server_locked(server);
31         *(__u8 *) (&(server->packet[server->current_size])) = x;
32         server->current_size += 1;
33         return;
34 }
35
36 static void ncp_add_word(struct ncp_server *server, __u16 x)
37 {
38         assert_server_locked(server);
39         put_unaligned(x, (__u16 *) (&(server->packet[server->current_size])));
40         server->current_size += 2;
41         return;
42 }
43
44 static void ncp_add_dword(struct ncp_server *server, __u32 x)
45 {
46         assert_server_locked(server);
47         put_unaligned(x, (__u32 *) (&(server->packet[server->current_size])));
48         server->current_size += 4;
49         return;
50 }
51
52 static void ncp_add_mem(struct ncp_server *server, const void *source, int size)
53 {
54         assert_server_locked(server);
55         memcpy(&(server->packet[server->current_size]), source, size);
56         server->current_size += size;
57         return;
58 }
59
60 static void ncp_add_mem_fromfs(struct ncp_server *server, const char *source, int size)
61 {
62         assert_server_locked(server);
63         copy_from_user(&(server->packet[server->current_size]), source, size);
64         server->current_size += size;
65         return;
66 }
67
68 static void ncp_add_pstring(struct ncp_server *server, const char *s)
69 {
70         int len = strlen(s);
71         assert_server_locked(server);
72         if (len > 255) {
73                 DPRINTK("ncpfs: string too long: %s\n", s);
74                 len = 255;
75         }
76         ncp_add_byte(server, len);
77         ncp_add_mem(server, s, len);
78         return;
79 }
80
81 static inline void ncp_init_request(struct ncp_server *server)
82 {
83         ncp_lock_server(server);
84
85         server->current_size = sizeof(struct ncp_request_header);
86         server->has_subfunction = 0;
87 }
88
89 static inline void ncp_init_request_s(struct ncp_server *server, int subfunction)
90 {
91         ncp_lock_server(server);
92         
93         server->current_size = sizeof(struct ncp_request_header) + 2;
94         ncp_add_byte(server, subfunction);
95
96         server->has_subfunction = 1;
97 }
98
99 static inline char *
100  ncp_reply_data(struct ncp_server *server, int offset)
101 {
102         return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
103 }
104
105 static __u8
106  ncp_reply_byte(struct ncp_server *server, int offset)
107 {
108         return get_unaligned((__u8 *) ncp_reply_data(server, offset));
109 }
110
111 static __u16
112  ncp_reply_word(struct ncp_server *server, int offset)
113 {
114         return get_unaligned((__u16 *) ncp_reply_data(server, offset));
115 }
116
117 static __u32
118  ncp_reply_dword(struct ncp_server *server, int offset)
119 {
120         return get_unaligned((__u32 *) ncp_reply_data(server, offset));
121 }
122
123 int
124 ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
125 {
126         int result;
127
128         ncp_init_request(server);
129         ncp_add_word(server, htons(size));
130
131         if ((result = ncp_request(server, 33)) != 0) {
132                 ncp_unlock_server(server);
133                 return result;
134         }
135         *target = min(ntohs(ncp_reply_word(server, 0)), size);
136
137         ncp_unlock_server(server);
138         return 0;
139 }
140
141
142 /* options: 
143  *      bit 0   ipx checksum
144  *      bit 1   packet signing
145  */
146 int
147 ncp_negotiate_size_and_options(struct ncp_server *server, 
148         int size, int options, int *ret_size, int *ret_options) {
149         int result;
150
151         /* there is minimum */
152         if (size < NCP_BLOCK_SIZE) size = NCP_BLOCK_SIZE;
153
154         ncp_init_request(server);
155         ncp_add_word(server, htons(size));
156         ncp_add_byte(server, options);
157         
158         if ((result = ncp_request(server, 0x61)) != 0)
159         {
160                 ncp_unlock_server(server);
161                 return result;
162         }
163
164         /* NCP over UDP returns 0 (!!!) */
165         result = ntohs(ncp_reply_word(server, 0));
166         if (result >= NCP_BLOCK_SIZE) size=min(result, size);
167         *ret_size = size;
168         *ret_options = ncp_reply_byte(server, 4);
169
170         ncp_unlock_server(server);
171         return 0;
172 }
173
174 int
175 ncp_get_volume_info_with_number(struct ncp_server *server, int n,
176                                     struct ncp_volume_info *target)
177 {
178         int result;
179         int len;
180
181         ncp_init_request_s(server, 44);
182         ncp_add_byte(server, n);
183
184         if ((result = ncp_request(server, 22)) != 0) {
185                 goto out;
186         }
187         target->total_blocks = ncp_reply_dword(server, 0);
188         target->free_blocks = ncp_reply_dword(server, 4);
189         target->purgeable_blocks = ncp_reply_dword(server, 8);
190         target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12);
191         target->total_dir_entries = ncp_reply_dword(server, 16);
192         target->available_dir_entries = ncp_reply_dword(server, 20);
193         target->sectors_per_block = ncp_reply_byte(server, 28);
194
195         memset(&(target->volume_name), 0, sizeof(target->volume_name));
196
197         result = -EIO;
198         len = ncp_reply_byte(server, 29);
199         if (len > NCP_VOLNAME_LEN) {
200                 DPRINTK("ncpfs: volume name too long: %d\n", len);
201                 goto out;
202         }
203         memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
204         result = 0;
205 out:
206         ncp_unlock_server(server);
207         return result;
208 }
209
210 int
211 ncp_close_file(struct ncp_server *server, const char *file_id)
212 {
213         int result;
214
215         ncp_init_request(server);
216         ncp_add_byte(server, 0);
217         ncp_add_mem(server, file_id, 6);
218
219         result = ncp_request(server, 66);
220         ncp_unlock_server(server);
221         return result;
222 }
223
224 int
225 ncp_make_closed(struct inode *inode)
226 {
227         int err;
228
229         err = 0;
230         down(&NCP_FINFO(inode)->open_sem);      
231         if (atomic_read(&NCP_FINFO(inode)->opened) == 1) {
232                 atomic_set(&NCP_FINFO(inode)->opened, 0);
233                 err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle);
234
235                 if (!err)
236                         PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n",
237                                 NCP_FINFO(inode)->volNumber,
238                                 NCP_FINFO(inode)->dirEntNum, err);
239         }
240         up(&NCP_FINFO(inode)->open_sem);
241         return err;
242 }
243
244 static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
245                                 __u32 dir_base, int have_dir_base, 
246                                 const char *path)
247 {
248         ncp_add_byte(server, vol_num);
249         ncp_add_dword(server, dir_base);
250         if (have_dir_base != 0) {
251                 ncp_add_byte(server, 1);        /* dir_base */
252         } else {
253                 ncp_add_byte(server, 0xff);     /* no handle */
254         }
255         if (path != NULL) {
256                 ncp_add_byte(server, 1);        /* 1 component */
257                 ncp_add_pstring(server, path);
258         } else {
259                 ncp_add_byte(server, 0);
260         }
261 }
262
263 static void ncp_extract_file_info(void *structure, struct nw_info_struct *target)
264 {
265         __u8 *name_len;
266         const int info_struct_size = sizeof(struct nw_info_struct) - 257;
267
268         memcpy(target, structure, info_struct_size);
269         name_len = structure + info_struct_size;
270         target->nameLen = *name_len;
271         memcpy(target->entryName, name_len + 1, *name_len);
272         target->entryName[*name_len] = '\0';
273         return;
274 }
275
276 /*
277  * Returns information for a (one-component) name relative to
278  * the specified directory.
279  */
280 int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path,
281                         struct nw_info_struct *target)
282 {
283         __u8  volnum = NCP_FINFO(dir)->volNumber;
284         __u32 dirent = NCP_FINFO(dir)->dirEntNum;
285         int result;
286
287         if (target == NULL) {
288                 printk(KERN_ERR "ncp_obtain_info: invalid call\n");
289                 return -EINVAL;
290         }
291         ncp_init_request(server);
292         ncp_add_byte(server, 6);        /* subfunction */
293         ncp_add_byte(server, server->name_space[volnum]);
294         ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
295         ncp_add_word(server, htons(0x0680));    /* get all */
296         ncp_add_dword(server, RIM_ALL);
297         ncp_add_handle_path(server, volnum, dirent, 1, path);
298
299         if ((result = ncp_request(server, 87)) != 0)
300                 goto out;
301         ncp_extract_file_info(ncp_reply_data(server, 0), target);
302
303 out:
304         ncp_unlock_server(server);
305         return result;
306 }
307
308 #ifdef CONFIG_NCPFS_NFS_NS
309 static int
310 ncp_obtain_DOS_dir_base(struct ncp_server *server,
311                 __u8 volnum, __u32 dirent,
312                 char *path, /* At most 1 component */
313                 __u32 *DOS_dir_base)
314 {
315         int result;
316
317         ncp_init_request(server);
318         ncp_add_byte(server, 6); /* subfunction */
319         ncp_add_byte(server, server->name_space[volnum]);
320         ncp_add_byte(server, server->name_space[volnum]);
321         ncp_add_word(server, htons(0x0680)); /* get all */
322         ncp_add_dword(server, RIM_DIRECTORY);
323         ncp_add_handle_path(server, volnum, dirent, 1, path);
324
325         if ((result = ncp_request(server, 87)) == 0)
326         {
327                 if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
328         }
329         ncp_unlock_server(server);
330         return result;
331 }
332 #endif /* CONFIG_NCPFS_NFS_NS */
333
334 static inline int
335 ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
336 {
337 #if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
338         int result;
339         __u8 *namespace;
340         __u16 no_namespaces;
341
342         ncp_init_request(server);
343         ncp_add_byte(server, 24);       /* Subfunction: Get Name Spaces Loaded */
344         ncp_add_word(server, 0);
345         ncp_add_byte(server, volume);
346
347         if ((result = ncp_request(server, 87)) != 0) {
348                 ncp_unlock_server(server);
349                 return NW_NS_DOS; /* not result ?? */
350         }
351
352         result = NW_NS_DOS;
353         no_namespaces = ncp_reply_word(server, 0);
354         namespace = ncp_reply_data(server, 2);
355
356         while (no_namespaces > 0) {
357                 DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume);
358
359 #ifdef CONFIG_NCPFS_NFS_NS
360                 if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS)) 
361                 {
362                         result = NW_NS_NFS;
363                         break;
364                 }
365 #endif  /* CONFIG_NCPFS_NFS_NS */
366 #ifdef CONFIG_NCPFS_OS2_NS
367                 if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
368                 {
369                         result = NW_NS_OS2;
370                 }
371 #endif  /* CONFIG_NCPFS_OS2_NS */
372                 namespace += 1;
373                 no_namespaces -= 1;
374         }
375         ncp_unlock_server(server);
376         return result;
377 #else   /* neither OS2 nor NFS - only DOS */
378         return NW_NS_DOS;
379 #endif  /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
380 }
381
382 static int
383 ncp_ObtainSpecificDirBase(struct ncp_server *server,
384                 __u8 nsSrc, __u8 nsDst, __u8 vol_num, __u32 dir_base,
385                 char *path, /* At most 1 component */
386                 __u32 *dirEntNum, __u32 *DosDirNum)
387 {
388         int result;
389
390         ncp_init_request(server);
391         ncp_add_byte(server, 6); /* subfunction */
392         ncp_add_byte(server, nsSrc);
393         ncp_add_byte(server, nsDst);
394         ncp_add_word(server, htons(0x0680)); /* get all */
395         ncp_add_dword(server, RIM_ALL);
396         ncp_add_handle_path(server, vol_num, dir_base, 1, path);
397
398         if ((result = ncp_request(server, 87)) != 0)
399         {
400                 ncp_unlock_server(server);
401                 return result;
402         }
403
404         if (dirEntNum)
405                 *dirEntNum = ncp_reply_dword(server, 0x30);
406         if (DosDirNum)
407                 *DosDirNum = ncp_reply_dword(server, 0x34);
408         ncp_unlock_server(server);
409         return 0;
410 }
411
412 int
413 ncp_mount_subdir(struct ncp_server *server, struct nw_info_struct *i,
414                         __u8 volNumber, __u8 srcNS, __u32 dirEntNum)
415 {
416         int dstNS;
417         int result;
418         __u32 newDirEnt;
419         __u32 newDosEnt;
420         
421         dstNS = ncp_get_known_namespace(server, volNumber);
422         if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, 
423                                       dirEntNum, NULL, &newDirEnt, &newDosEnt)) != 0)
424         {
425                 return result;
426         }
427         server->name_space[volNumber] = dstNS;
428         i->volNumber = volNumber;
429         i->dirEntNum = newDirEnt;
430         i->DosDirNum = newDosEnt;
431         server->m.mounted_vol[1] = 0;
432         server->m.mounted_vol[0] = 'X';
433         return 0;
434 }
435
436 int 
437 ncp_lookup_volume(struct ncp_server *server, char *volname,
438                       struct nw_info_struct *target)
439 {
440         int result;
441         int volnum;
442
443         DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname);
444
445         ncp_init_request(server);
446         ncp_add_byte(server, 22);       /* Subfunction: Generate dir handle */
447         ncp_add_byte(server, 0);        /* DOS namespace */
448         ncp_add_byte(server, 0);        /* reserved */
449         ncp_add_byte(server, 0);        /* reserved */
450         ncp_add_byte(server, 0);        /* reserved */
451
452         ncp_add_byte(server, 0);        /* faked volume number */
453         ncp_add_dword(server, 0);       /* faked dir_base */
454         ncp_add_byte(server, 0xff);     /* Don't have a dir_base */
455         ncp_add_byte(server, 1);        /* 1 path component */
456         ncp_add_pstring(server, volname);
457
458         if ((result = ncp_request(server, 87)) != 0) {
459                 ncp_unlock_server(server);
460                 return result;
461         }
462         memset(target, 0, sizeof(*target));
463         target->DosDirNum = target->dirEntNum = ncp_reply_dword(server, 4);
464         target->volNumber = volnum = ncp_reply_byte(server, 8);
465         ncp_unlock_server(server);
466
467         server->name_space[volnum] = ncp_get_known_namespace(server, volnum);
468
469         DPRINTK("lookup_vol: namespace[%d] = %d\n",
470                 volnum, server->name_space[volnum]);
471
472         target->nameLen = strlen(volname);
473         memcpy(target->entryName, volname, target->nameLen+1);
474         target->attributes = aDIR;
475         /* set dates to Jan 1, 1986  00:00 */
476         target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
477         target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
478         return 0;
479 }
480
481 int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
482                                             struct inode *dir,
483                                             const char *path,
484                                             __u32 info_mask,
485                                             const struct nw_modify_dos_info *info)
486 {
487         __u8  volnum = NCP_FINFO(dir)->volNumber;
488         __u32 dirent = NCP_FINFO(dir)->dirEntNum;
489         int result;
490
491         ncp_init_request(server);
492         ncp_add_byte(server, 7);        /* subfunction */
493         ncp_add_byte(server, server->name_space[volnum]);
494         ncp_add_byte(server, 0);        /* reserved */
495         ncp_add_word(server, htons(0x0680));    /* search attribs: all */
496
497         ncp_add_dword(server, info_mask);
498         ncp_add_mem(server, info, sizeof(*info));
499         ncp_add_handle_path(server, volnum, dirent, 1, path);
500
501         result = ncp_request(server, 87);
502         ncp_unlock_server(server);
503         return result;
504 }
505
506 int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
507                                        struct inode *dir,
508                                        __u32 info_mask,
509                                        const struct nw_modify_dos_info *info)
510 {
511         return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
512                 info_mask, info);
513 }
514
515 static int
516 ncp_DeleteNSEntry(struct ncp_server *server,
517                   __u8 have_dir_base, __u8 volnum, __u32 dirent,
518                   char* name, __u8 ns, int attr)
519 {
520         int result;
521
522         ncp_init_request(server);
523         ncp_add_byte(server, 8);        /* subfunction */
524         ncp_add_byte(server, ns);
525         ncp_add_byte(server, 0);        /* reserved */
526         ncp_add_word(server, attr);     /* search attribs: all */
527         ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
528
529         result = ncp_request(server, 87);
530         ncp_unlock_server(server);
531         return result;
532 }
533
534 int
535 ncp_del_file_or_subdir2(struct ncp_server *server,
536                         struct dentry *dentry)
537 {
538         struct inode *inode = dentry->d_inode;
539         __u8  volnum;
540         __u32 dirent;
541
542         if (!inode) {
543 #if CONFIG_NCPFS_DEBUGDENTRY
544                 PRINTK("ncpfs: ncpdel2: dentry->d_inode == NULL\n");
545 #endif
546                 return 0xFF;    /* Any error */
547         }
548         volnum = NCP_FINFO(inode)->volNumber;
549         dirent = NCP_FINFO(inode)->DosDirNum;
550         return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
551 }
552
553 int
554 ncp_del_file_or_subdir(struct ncp_server *server,
555                        struct inode *dir, char *name)
556 {
557         __u8  volnum = NCP_FINFO(dir)->volNumber;
558         __u32 dirent = NCP_FINFO(dir)->dirEntNum;
559
560 #ifdef CONFIG_NCPFS_NFS_NS
561         if (server->name_space[volnum]==NW_NS_NFS)
562         {
563                 int result;
564  
565                 result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent);
566                 if (result) return result;
567                 return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
568         }
569         else
570 #endif  /* CONFIG_NCPFS_NFS_NS */
571                 return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], htons(0x0680));
572 }
573
574 static inline void ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6])
575 {
576         __u16 *dest = (__u16 *) ret;
577         memcpy(ret + 2, &sfd, 4);
578         dest[0] = cpu_to_le16((le16_to_cpu(dest[1]) + le16_to_cpu(1)));
579         return;
580 }
581
582 /* If both dir and name are NULL, then in target there's already a
583    looked-up entry that wants to be opened. */
584 int ncp_open_create_file_or_subdir(struct ncp_server *server,
585                                    struct inode *dir, char *name,
586                                    int open_create_mode,
587                                    __u32 create_attributes,
588                                    int desired_acc_rights,
589                                    struct ncp_entry_info *target)
590 {
591         __u16 search_attribs = ntohs(0x0600);
592         __u8  volnum = target->i.volNumber;
593         __u32 dirent = target->i.dirEntNum;
594         int result;
595
596         if (dir)
597         {
598                 volnum = NCP_FINFO(dir)->volNumber;
599                 dirent = NCP_FINFO(dir)->dirEntNum;
600         }
601
602         if ((create_attributes & aDIR) != 0) {
603                 search_attribs |= ntohs(0x0080);
604         }
605         ncp_init_request(server);
606         ncp_add_byte(server, 1);        /* subfunction */
607         ncp_add_byte(server, server->name_space[volnum]);
608         ncp_add_byte(server, open_create_mode);
609         ncp_add_word(server, search_attribs);
610         ncp_add_dword(server, RIM_ALL);
611         ncp_add_dword(server, create_attributes);
612         /* The desired acc rights seem to be the inherited rights mask
613            for directories */
614         ncp_add_word(server, desired_acc_rights);
615         ncp_add_handle_path(server, volnum, dirent, 1, name);
616
617         if ((result = ncp_request(server, 87)) != 0)
618                 goto out;
619         if (!(create_attributes & aDIR))
620                 target->opened = 1;
621         target->server_file_handle = ncp_reply_dword(server, 0);
622         target->open_create_action = ncp_reply_byte(server, 4);
623
624         /* in target there's a new finfo to fill */
625         ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
626         ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
627
628 out:
629         ncp_unlock_server(server);
630         return result;
631 }
632
633 int
634 ncp_initialize_search(struct ncp_server *server, struct inode *dir,
635                         struct nw_search_sequence *target)
636 {
637         __u8  volnum = NCP_FINFO(dir)->volNumber;
638         __u32 dirent = NCP_FINFO(dir)->dirEntNum;
639         int result;
640
641         ncp_init_request(server);
642         ncp_add_byte(server, 2);        /* subfunction */
643         ncp_add_byte(server, server->name_space[volnum]);
644         ncp_add_byte(server, 0);        /* reserved */
645         ncp_add_handle_path(server, volnum, dirent, 1, NULL);
646
647         result = ncp_request(server, 87);
648         if (result)
649                 goto out;
650         memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
651
652 out:
653         ncp_unlock_server(server);
654         return result;
655 }
656
657 /* Search for everything */
658 int ncp_search_for_file_or_subdir(struct ncp_server *server,
659                                   struct nw_search_sequence *seq,
660                                   struct nw_info_struct *target)
661 {
662         int result;
663
664         ncp_init_request(server);
665         ncp_add_byte(server, 3);        /* subfunction */
666         ncp_add_byte(server, server->name_space[seq->volNumber]);
667         ncp_add_byte(server, 0);        /* data stream (???) */
668         ncp_add_word(server, htons(0x0680));    /* Search attribs */
669         ncp_add_dword(server, RIM_ALL);         /* return info mask */
670         ncp_add_mem(server, seq, 9);
671 #ifdef CONFIG_NCPFS_NFS_NS
672         if (server->name_space[seq->volNumber] == NW_NS_NFS) {
673                 ncp_add_byte(server, 0);        /* 0 byte pattern */
674         } else 
675 #endif
676         {
677                 ncp_add_byte(server, 2);        /* 2 byte pattern */
678                 ncp_add_byte(server, 0xff);     /* following is a wildcard */
679                 ncp_add_byte(server, '*');
680         }
681         
682         if ((result = ncp_request(server, 87)) != 0)
683                 goto out;
684         memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
685         ncp_extract_file_info(ncp_reply_data(server, 10), target);
686
687 out:
688         ncp_unlock_server(server);
689         return result;
690 }
691
692 int
693 ncp_RenameNSEntry(struct ncp_server *server,
694                   struct inode *old_dir, char *old_name, int old_type,
695                   struct inode *new_dir, char *new_name)
696 {
697         int result = -EINVAL;
698
699         if ((old_dir == NULL) || (old_name == NULL) ||
700             (new_dir == NULL) || (new_name == NULL))
701                 goto out;
702
703         ncp_init_request(server);
704         ncp_add_byte(server, 4);        /* subfunction */
705         ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
706         ncp_add_byte(server, 1);        /* rename flag */
707         ncp_add_word(server, old_type); /* search attributes */
708
709         /* source Handle Path */
710         ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
711         ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
712         ncp_add_byte(server, 1);
713         ncp_add_byte(server, 1);        /* 1 source component */
714
715         /* dest Handle Path */
716         ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
717         ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
718         ncp_add_byte(server, 1);
719         ncp_add_byte(server, 1);        /* 1 destination component */
720
721         /* source path string */
722         ncp_add_pstring(server, old_name);
723         /* dest path string */
724         ncp_add_pstring(server, new_name);
725
726         result = ncp_request(server, 87);
727         ncp_unlock_server(server);
728 out:
729         return result;
730 }
731
732 int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
733                                 struct inode *old_dir, char *old_name,
734                                 struct inode *new_dir, char *new_name)
735 {
736         int result;
737         int old_type = htons(0x0600);
738
739 /* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
740         result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
741                                            new_dir, new_name);
742         if (result == 0xFF)     /* File Not Found, try directory */
743         {
744                 old_type = htons(0x1600);
745                 result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
746                                                    new_dir, new_name);
747         }
748         if (result != 0x92) return result;      /* All except NO_FILES_RENAMED */
749         result = ncp_del_file_or_subdir(server, new_dir, new_name);
750         if (result != 0) return -EACCES;
751         result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
752                                            new_dir, new_name);
753         return result;
754 }
755         
756
757 /* We have to transfer to/from user space */
758 int
759 ncp_read_kernel(struct ncp_server *server, const char *file_id,
760              __u32 offset, __u16 to_read, char *target, int *bytes_read)
761 {
762         char *source;
763         int result;
764
765         ncp_init_request(server);
766         ncp_add_byte(server, 0);
767         ncp_add_mem(server, file_id, 6);
768         ncp_add_dword(server, htonl(offset));
769         ncp_add_word(server, htons(to_read));
770
771         if ((result = ncp_request(server, 72)) != 0) {
772                 goto out;
773         }
774         *bytes_read = ntohs(ncp_reply_word(server, 0));
775         source = ncp_reply_data(server, 2 + (offset & 1));
776
777         memcpy(target, source, *bytes_read);
778 out:
779         ncp_unlock_server(server);
780         return result;
781 }
782
783 /* There is a problem... egrep and some other silly tools do:
784         x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
785         read(<ncpfs fd>, x, 32768);
786    Now copying read result by copy_to_user causes pagefault. This pagefault
787    could not be handled because of server was locked due to read. So we have
788    to use temporary buffer. So ncp_unlock_server must be done before
789    copy_to_user (and for write, copy_from_user must be done before 
790    ncp_init_request... same applies for send raw packet ioctl). Because of
791    file is normally read in bigger chunks, caller provides kmalloced 
792    (vmalloced) chunk of memory with size >= to_read...
793  */
794 int
795 ncp_read_bounce(struct ncp_server *server, const char *file_id,
796          __u32 offset, __u16 to_read, char *target, int *bytes_read,
797          void* bounce, __u32 bufsize)
798 {
799         int result;
800
801         ncp_init_request(server);
802         ncp_add_byte(server, 0);
803         ncp_add_mem(server, file_id, 6);
804         ncp_add_dword(server, htonl(offset));
805         ncp_add_word(server, htons(to_read));
806         result = ncp_request2(server, 72, bounce, bufsize);
807         ncp_unlock_server(server);
808         if (!result) {
809                 int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce + 
810                           sizeof(struct ncp_reply_header))));
811                 result = -EIO;
812                 if (len <= to_read) {
813                         char* source;
814
815                         source = (char*)bounce + 
816                                  sizeof(struct ncp_reply_header) + 2 + 
817                                  (offset & 1);
818                         *bytes_read = len;
819                         result = 0;
820                         if (copy_to_user(target, source, len))
821                                 result = -EFAULT;
822                 }
823         }
824         return result;
825 }
826
827 int
828 ncp_write_kernel(struct ncp_server *server, const char *file_id,
829                  __u32 offset, __u16 to_write,
830                  const char *source, int *bytes_written)
831 {
832         int result;
833
834         ncp_init_request(server);
835         ncp_add_byte(server, 0);
836         ncp_add_mem(server, file_id, 6);
837         ncp_add_dword(server, htonl(offset));
838         ncp_add_word(server, htons(to_write));
839         ncp_add_mem(server, source, to_write);
840         
841         if ((result = ncp_request(server, 73)) == 0)
842                 *bytes_written = to_write;
843         ncp_unlock_server(server);
844         return result;
845 }
846
847 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
848 int
849 ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
850           __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
851 {
852         int result;
853
854         ncp_init_request(server);
855         ncp_add_byte(server, locktype);
856         ncp_add_mem(server, file_id, 6);
857         ncp_add_dword(server, htonl(offset));
858         ncp_add_dword(server, htonl(length));
859         ncp_add_word(server, htons(timeout));
860
861         if ((result = ncp_request(server, 0x1A)) != 0)
862         {
863                 ncp_unlock_server(server);
864                 return result;
865         }
866         ncp_unlock_server(server);
867         return 0;
868 }
869
870 int
871 ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
872           __u32 offset, __u32 length)
873 {
874         int result;
875
876         ncp_init_request(server);
877         ncp_add_byte(server, 0);        /* who knows... lanalyzer says that */
878         ncp_add_mem(server, file_id, 6);
879         ncp_add_dword(server, htonl(offset));
880         ncp_add_dword(server, htonl(length));
881
882         if ((result = ncp_request(server, 0x1E)) != 0)
883         {
884                 ncp_unlock_server(server);
885                 return result;
886         }
887         ncp_unlock_server(server);
888         return 0;
889 }
890 #endif  /* CONFIG_NCPFS_IOCTL_LOCKING */
891
892 #ifdef CONFIG_NCPFS_NLS
893 /* This are the NLS conversion routines with inspirations and code parts
894  * from the vfat file system and hints from Petr Vandrovec.
895  */
896
897 inline unsigned char
898 ncp__tolower(struct nls_table *t, unsigned char c)
899 {
900         unsigned char nc = t->charset2lower[c];
901
902         return nc ? nc : c;
903 }
904
905 inline unsigned char
906 ncp__toupper(struct nls_table *t, unsigned char c)
907 {
908         unsigned char nc = t->charset2upper[c];
909
910         return nc ? nc : c;
911 }
912
913 int
914 ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
915                 const unsigned char *iname, unsigned int ilen, int cc)
916 {
917         struct nls_table *in = server->nls_io;
918         struct nls_table *out = server->nls_vol;
919         unsigned char *vname_start;
920         unsigned char *vname_end;
921         const unsigned char *iname_end;
922
923         iname_end = iname + ilen;
924         vname_start = vname;
925         vname_end = vname + *vlen - 1;
926
927         while (iname < iname_end) {
928                 int chl;
929                 wchar_t ec;
930
931                 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
932                         int k;
933
934                         k = utf8_mbtowc(&ec, iname, iname_end - iname);
935                         if (k < 0)
936                                 return -EINVAL;
937                         iname += k;
938                 } else {
939                         if (*iname == NCP_ESC) {
940                                 int k;
941
942                                 if (iname_end - iname < 5)
943                                         goto nospec;
944
945                                 ec = 0;
946                                 for (k = 1; k < 5; k++) {
947                                         unsigned char nc;
948
949                                         nc = iname[k] - '0';
950                                         if (nc >= 10) {
951                                                 nc -= 'A' - '0' - 10;
952                                                 if ((nc < 10) || (nc > 15)) {
953                                                         goto nospec;
954                                                 }
955                                         }
956                                         ec = (ec << 4) | nc;
957                                 }
958                                 iname += 5;
959                         } else {
960 nospec:;                        
961                                 if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0)
962                                         return chl;
963                                 iname += chl;
964                         }
965                 }
966
967                 /* unitoupper should be here! */
968
969                 chl = out->uni2char(ec, vname, vname_end - vname);
970                 if (chl < 0)
971                         return chl;
972
973                 /* this is wrong... */
974                 if (cc) {
975                         int chi;
976
977                         for (chi = 0; chi < chl; chi++){
978                                 vname[chi] = ncp_toupper(out, vname[chi]);
979                         }
980                 }
981                 vname += chl;
982         }
983
984         *vname = 0;
985         *vlen = vname - vname_start;
986         return 0;
987 }
988
989 int
990 ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
991                 const unsigned char *vname, unsigned int vlen, int cc)
992 {
993         struct nls_table *in = server->nls_vol;
994         struct nls_table *out = server->nls_io;
995         const unsigned char *vname_end;
996         unsigned char *iname_start;
997         unsigned char *iname_end;
998         unsigned char *vname_cc;
999         int err;
1000
1001         vname_cc = NULL;
1002
1003         if (cc) {
1004                 int i;
1005
1006                 /* this is wrong! */
1007                 vname_cc = kmalloc(vlen, GFP_KERNEL);
1008                 if (!vname_cc)
1009                         return -ENOMEM;
1010                 for (i = 0; i < vlen; i++)
1011                         vname_cc[i] = ncp_tolower(in, vname[i]);
1012                 vname = vname_cc;
1013         }
1014
1015         iname_start = iname;
1016         iname_end = iname + *ilen - 1;
1017         vname_end = vname + vlen;
1018
1019         while (vname < vname_end) {
1020                 wchar_t ec;
1021                 int chl;
1022
1023                 if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) {
1024                         err = chl;
1025                         goto quit;
1026                 }
1027                 vname += chl;
1028
1029                 /* unitolower should be here! */
1030
1031                 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
1032                         int k;
1033
1034                         k = utf8_wctomb(iname, ec, iname_end - iname);
1035                         if (k < 0) {
1036                                 err = -ENAMETOOLONG;
1037                                 goto quit;
1038                         }
1039                         iname += k;
1040                 } else {
1041                         if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) {
1042                                 iname += chl;
1043                         } else {
1044                                 int k;
1045
1046                                 if (iname_end - iname < 5) {
1047                                         err = -ENAMETOOLONG;
1048                                         goto quit;
1049                                 }
1050                                 *iname = NCP_ESC;
1051                                 for (k = 4; k > 0; k--) {
1052                                         unsigned char v;
1053                                         
1054                                         v = (ec & 0xF) + '0';
1055                                         if (v > '9') {
1056                                                 v += 'A' - '9' - 1;
1057                                         }
1058                                         iname[k] = v;
1059                                         ec >>= 4;
1060                                 }
1061                                 iname += 5;
1062                         }
1063                 }
1064         }
1065
1066         *iname = 0;
1067         *ilen = iname - iname_start;
1068         err = 0;
1069 quit:;
1070         if (cc)
1071                 kfree(vname_cc);
1072         return err;
1073 }
1074
1075 #else
1076
1077 int
1078 ncp__io2vol(unsigned char *vname, unsigned int *vlen,
1079                 const unsigned char *iname, unsigned int ilen, int cc)
1080 {
1081         int i;
1082
1083         if (*vlen <= ilen)
1084                 return -ENAMETOOLONG;
1085
1086         if (cc)
1087                 for (i = 0; i < ilen; i++) {
1088                         *vname = toupper(*iname);
1089                         vname++;
1090                         iname++;
1091                 }
1092         else {
1093                 memmove(vname, iname, ilen);
1094                 vname += ilen;
1095         }
1096
1097         *vlen = ilen;
1098         *vname = 0;
1099         return 0;
1100 }
1101
1102 int
1103 ncp__vol2io(unsigned char *iname, unsigned int *ilen,
1104                 const unsigned char *vname, unsigned int vlen, int cc)
1105 {
1106         int i;
1107
1108         if (*ilen <= vlen)
1109                 return -ENAMETOOLONG;
1110
1111         if (cc)
1112                 for (i = 0; i < vlen; i++) {
1113                         *iname = tolower(*vname);
1114                         iname++;
1115                         vname++;
1116                 }
1117         else {
1118                 memmove(iname, vname, vlen);
1119                 iname += vlen;
1120         }
1121
1122         *ilen = vlen;
1123         *iname = 0;
1124         return 0;
1125 }
1126
1127 #endif
1128
1129 inline int
1130 ncp_strnicmp(struct nls_table *t, const unsigned char *s1,
1131                                         const unsigned char *s2, int n)
1132 {
1133         int i;
1134
1135         for (i=0; i<n; i++)
1136                 if (ncp_tolower(t, s1[i]) != ncp_tolower(t, s2[i]))
1137                         return 1;
1138
1139         return 0;
1140 }