OSDN Git Service

* select.cc (select_stuff::wait): Temporarily disallow APCS.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / fhandler_netdrive.cc
1 /* fhandler_netdrive.cc: fhandler for // and //MACHINE handling
2
3    Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <stdlib.h>
13 #include "cygerrno.h"
14 #include "security.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "cygheap.h"
19 #include "cygthread.h"
20 #include <winnetwk.h>
21
22 #include <dirent.h>
23
24 enum
25   {
26     GET_RESOURCE_OPENENUM = 1,
27     GET_RESOURCE_OPENENUMTOP = 2,
28     GET_RESOURCE_ENUM = 3
29   };
30
31 struct netdriveinf
32   {
33     int what;
34     int ret;
35     PVOID in;
36     PVOID out;
37     DWORD outsize;
38     HANDLE sem;
39   };
40
41 struct net_hdls
42   {
43     HANDLE net;
44     HANDLE dom;
45   };
46
47 static DWORD WINAPI
48 thread_netdrive (void *arg)
49 {
50   netdriveinf *ndi = (netdriveinf *) arg;
51   char provider[256], *dummy = NULL;
52   LPNETRESOURCE nro;
53   DWORD cnt, size;
54   struct net_hdls *nh;
55
56   ReleaseSemaphore (ndi->sem, 1, NULL);
57   switch (ndi->what)
58     {
59     case GET_RESOURCE_OPENENUMTOP:
60       nro = (LPNETRESOURCE) alloca (size = 4096);
61       nh = (struct net_hdls *) ndi->out;
62       ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider,
63                                       (size = 256, &size));
64       if (ndi->ret != NO_ERROR)
65         break;
66       memset (nro, 0, sizeof *nro);
67       nro->dwScope = RESOURCE_GLOBALNET;
68       nro->dwType = RESOURCETYPE_ANY;
69       nro->dwDisplayType = RESOURCEDISPLAYTYPE_GROUP;
70       nro->dwUsage = RESOURCEUSAGE_RESERVED | RESOURCEUSAGE_CONTAINER;
71       nro->lpRemoteName = provider;
72       nro->lpProvider = provider;
73       ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
74                                RESOURCEUSAGE_ALL, nro, &nh->net);
75       if (ndi->ret != NO_ERROR)
76         break;
77       while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro,
78                                 (size = 4096, &size))) == NO_ERROR)
79         {
80           ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
81                                    RESOURCEUSAGE_ALL, nro, &nh->dom);
82           if (ndi->ret == NO_ERROR)
83             break;
84         }
85       break;
86     case GET_RESOURCE_OPENENUM:
87       nro = (LPNETRESOURCE) alloca (size = 4096);
88       nh = (struct net_hdls *) ndi->out;
89       ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider,
90                                       (size = 256, &size));
91       if (ndi->ret != NO_ERROR)
92         break;
93       ((LPNETRESOURCE) ndi->in)->lpProvider = provider;
94       ndi->ret = WNetGetResourceInformation ((LPNETRESOURCE) ndi->in,
95                                              nro, &size, &dummy);
96       if (ndi->ret != NO_ERROR)
97         break;
98       ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
99                                RESOURCEUSAGE_ALL, nro, &nh->dom);
100       break;
101     case GET_RESOURCE_ENUM:
102       nh = (struct net_hdls *) ndi->in;
103       if (!nh->dom)
104         {
105           ndi->ret = ERROR_NO_MORE_ITEMS;
106           break;
107         }
108       while ((ndi->ret = WNetEnumResource (nh->dom, (cnt = 1, &cnt),
109                                            (LPNETRESOURCE) ndi->out,
110                                            &ndi->outsize)) != NO_ERROR
111              && nh->net)
112         {
113           WNetCloseEnum (nh->dom);
114           nh->dom = NULL;
115           nro = (LPNETRESOURCE) alloca (size = 4096);
116           while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro,
117                                              (size = 4096, &size))) == NO_ERROR)
118             {
119               ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
120                                        RESOURCEUSAGE_ALL, nro, &nh->dom);
121               if (ndi->ret == NO_ERROR)
122                 break;
123             }
124           if (ndi->ret != NO_ERROR)
125             break;
126         }
127       break;
128     }
129   ReleaseSemaphore (ndi->sem, 1, NULL);
130   return 0;
131 }
132
133 static DWORD
134 create_thread_and_wait (int what, PVOID in, PVOID out, DWORD outsize,
135                         const char *name)
136 {
137   netdriveinf ndi = { what, 0, in, out, outsize,
138                       CreateSemaphore (&sec_none_nih, 0, 2, NULL) };
139   cygthread *thr = new cygthread (thread_netdrive, &ndi, name);
140   if (thr->detach (ndi.sem))
141     ndi.ret = ERROR_OPERATION_ABORTED;
142   CloseHandle (ndi.sem);
143   return ndi.ret;
144 }
145
146 /* Returns 0 if path doesn't exist, >0 if path is a directory,
147    -1 if path is a file, -2 if it's a symlink.  */
148 virtual_ftype_t
149 fhandler_netdrive::exists ()
150 {
151   char *to;
152   const char *from;
153   size_t len = strlen (get_name ());
154   if (len == 2)
155     return virt_rootdir;
156   char namebuf[len + 1];
157   for (to = namebuf, from = get_name (); *from; to++, from++)
158     *to = (*from == '/') ? '\\' : *from;
159   *to = '\0';
160
161   struct net_hdls nh =  { NULL, NULL };
162   NETRESOURCE nr = {0};
163   nr.dwType = RESOURCETYPE_DISK;
164   nr.lpRemoteName = namebuf;
165   DWORD ret = create_thread_and_wait (GET_RESOURCE_OPENENUM,
166                                       &nr, &nh, 0, "WNetOpenEnum");
167   if (nh.dom)
168     WNetCloseEnum (nh.dom);
169   return ret != NO_ERROR ? virt_none : virt_directory;
170 }
171
172 fhandler_netdrive::fhandler_netdrive ():
173   fhandler_virtual ()
174 {
175 }
176
177 int
178 fhandler_netdrive::fstat (struct __stat64 *buf)
179 {
180   const char *path = get_name ();
181   debug_printf ("fstat (%s)", path);
182
183   fhandler_base::fstat (buf);
184
185   buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
186   buf->st_ino = get_ino ();
187
188   return 0;
189 }
190
191 int
192 fhandler_netdrive::readdir (DIR *dir, dirent *de)
193 {
194   NETRESOURCE *nro;
195   DWORD ret;
196   int res;
197
198   if (!dir->__d_position)
199     {
200       size_t len = strlen (get_name ());
201       char *namebuf = NULL;
202       NETRESOURCE nr = { 0 };
203       struct net_hdls *nh;
204
205       if (len != 2)     /* // */
206         {
207           const char *from;
208           char *to;
209           namebuf = (char *) alloca (len + 1);
210           for (to = namebuf, from = get_name (); *from; to++, from++)
211             *to = (*from == '/') ? '\\' : *from;
212           *to = '\0';
213         }
214
215       nr.lpRemoteName = namebuf;
216       nr.dwType = RESOURCETYPE_DISK;
217       nh = (struct net_hdls *) ccalloc (HEAP_FHANDLER, 1, sizeof *nh);
218       ret = create_thread_and_wait (len == 2 ? GET_RESOURCE_OPENENUMTOP
219                                              : GET_RESOURCE_OPENENUM,
220                                     &nr, nh, 0, "WNetOpenEnum");
221       if (ret != NO_ERROR)
222         {
223           dir->__handle = INVALID_HANDLE_VALUE;
224           res = geterrno_from_win_error (ret);
225           goto out;
226         }
227       dir->__handle = (HANDLE) nh;
228     }
229   ret = create_thread_and_wait (GET_RESOURCE_ENUM, dir->__handle,
230                                 nro = (LPNETRESOURCE) alloca (16384),
231                                 16384, "WnetEnumResource");
232   if (ret != NO_ERROR)
233     res = geterrno_from_win_error (ret);
234   else
235     {
236       dir->__d_position++;
237       char *bs = strrchr (nro->lpRemoteName, '\\');
238       strcpy (de->d_name, bs ? bs + 1 : nro->lpRemoteName);
239       if (strlen (get_name ()) == 2)
240         {
241           strlwr (de->d_name);
242           de->d_ino = hash_path_name (get_ino (), de->d_name);
243         }
244       else
245         {
246           de->d_ino = readdir_get_ino (nro->lpRemoteName, false);
247           /* We can't trust remote inode numbers of only 32 bit.  That means,
248              remote NT4 NTFS, as well as shares of Samba version < 3.0. */
249           if (de->d_ino <= UINT_MAX)
250             de->d_ino = hash_path_name (0, nro->lpRemoteName);
251         }
252       de->d_type = DT_DIR;
253
254       res = 0;
255     }
256 out:
257   syscall_printf ("%d = readdir(%p, %p)", res, dir, de);
258   return res;
259 }
260
261 void
262 fhandler_netdrive::seekdir (DIR *dir, long pos)
263 {
264   rewinddir (dir);
265   if (pos < 0)
266     return;
267   while (dir->__d_position < pos)
268     if (readdir (dir, dir->__d_dirent))
269       break;
270 }
271
272 void
273 fhandler_netdrive::rewinddir (DIR *dir)
274 {
275   if (dir->__handle != INVALID_HANDLE_VALUE)
276     {
277       struct net_hdls *nh = (struct net_hdls *) dir->__handle;
278       if (nh->dom)
279         WNetCloseEnum (nh->dom);
280       if (nh->net)
281         WNetCloseEnum (nh->net);
282       cfree (nh);
283     }
284   dir->__handle = INVALID_HANDLE_VALUE;
285   return fhandler_virtual::rewinddir (dir);
286 }
287
288 int
289 fhandler_netdrive::closedir (DIR *dir)
290 {
291   rewinddir (dir);
292   return fhandler_virtual::closedir (dir);
293 }
294
295 int
296 fhandler_netdrive::open (int flags, mode_t mode)
297 {
298   int res = fhandler_virtual::open (flags, mode);
299   if (!res)
300     goto out;
301
302   nohandle (true);
303
304   if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
305     {
306       set_errno (EEXIST);
307       res = 0;
308       goto out;
309     }
310   else if (flags & O_WRONLY)
311     {
312       set_errno (EISDIR);
313       res = 0;
314       goto out;
315     }
316
317   res = 1;
318   set_flags ((flags & ~O_TEXT) | O_BINARY | O_DIROPEN);
319   set_open_status ();
320 out:
321   syscall_printf ("%d = fhandler_netdrive::open(%p, %d)", res, flags, mode);
322   return res;
323 }
324