Windows FindACLCache must hold scp write locked
[openafs.git] / src / WINNT / afsd / cm_volstat.c
1 /*
2  * Copyright (c) 2007-2010 Secure Endpoints Inc.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Neither the name of the Secure Endpoints Inc. nor the names of its
13  *       contributors may be used to endorse or promote products derived
14  *       from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /* This source file provides the declarations
30  * which specify the AFS Cache Manager Volume Status Event
31  * Notification API
32  */
33
34 #include <afsconfig.h>
35 #include <afs/param.h>
36 #include <roken.h>
37
38 #include <afs/stds.h>
39
40 #include <windows.h>
41 #include <winsock2.h>
42 #include <nb30.h>
43 #include <string.h>
44 #include <malloc.h>
45 #include "afsd.h"
46 #include "smb.h"
47 #include <WINNT/afsreg.h>
48
49 extern DWORD RDR_NetworkAddrChange(void);
50 extern DWORD RDR_VolumeStatus(ULONG cellID, ULONG volID, BOOLEAN online);
51 extern DWORD RDR_NetworkStatus(BOOLEAN status);
52
53 HMODULE hVolStatus = NULL;
54 dll_VolStatus_Funcs_t dll_funcs;
55 cm_VolStatus_Funcs_t cm_funcs;
56
57 static char volstat_NetbiosName[64] = "";
58
59 static DWORD RDR_Notifications = 0;
60
61 rdr_volstat_evt_t *rdr_evtH = NULL;
62 rdr_volstat_evt_t *rdr_evtT = NULL;
63
64 static EVENT_HANDLE rdr_q_event = NULL;
65
66 static osi_mutex_t rdr_evt_lock;
67
68 void
69 cm_VolStatus_SetRDRNotifications(DWORD onoff)
70 {
71     RDR_Notifications = onoff;
72 }
73
74 afs_uint32
75 cm_VolStatus_Active(void)
76 {
77     return (hVolStatus != NULL);
78 }
79
80 /* This function is used to load any Volume Status Handlers
81  * and their associated function pointers.
82  */
83 long
84 cm_VolStatus_Initialization(void)
85 {
86     long (__fastcall * dll_VolStatus_Initialization)(dll_VolStatus_Funcs_t * dll_funcs, cm_VolStatus_Funcs_t *cm_funcs) = NULL;
87     long code = 0;
88     HKEY parmKey;
89     DWORD dummyLen;
90     char wd[MAX_PATH+1] = "";
91
92     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
93                          0, KEY_QUERY_VALUE, &parmKey);
94     if (code == ERROR_SUCCESS) {
95         dummyLen = sizeof(wd);
96         code = RegQueryValueEx(parmKey, "VolStatusHandler", NULL, NULL,
97                                 (BYTE *) &wd, &dummyLen);
98
99         if (code == ERROR_SUCCESS) {
100             dummyLen = sizeof(volstat_NetbiosName);
101             code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,
102                                    (BYTE *)volstat_NetbiosName, &dummyLen);
103         }
104         if (code == ERROR_SUCCESS && wd[0])
105             hVolStatus = LoadLibrary(wd);
106
107         dummyLen = sizeof(wd);
108         code = RegQueryValueEx(parmKey, "RDRVolStatNotify", NULL, NULL,
109                                 (BYTE *) &RDR_Notifications, &dummyLen);
110
111         RegCloseKey (parmKey);
112     }
113
114     if (hVolStatus) {
115         (FARPROC) dll_VolStatus_Initialization = GetProcAddress(hVolStatus, "@VolStatus_Initialization@8");
116         if (dll_VolStatus_Initialization) {
117             cm_funcs.version = CM_VOLSTATUS_FUNCS_VERSION;
118             cm_funcs.cm_VolStatus_Path_To_ID = cm_VolStatus_Path_To_ID;
119             cm_funcs.cm_VolStatus_Path_To_DFSlink = cm_VolStatus_Path_To_DFSlink;
120
121             dll_funcs.version = DLL_VOLSTATUS_FUNCS_VERSION;
122             code = dll_VolStatus_Initialization(&dll_funcs, &cm_funcs);
123         }
124
125         if (dll_VolStatus_Initialization == NULL || code != 0 ||
126             dll_funcs.version != DLL_VOLSTATUS_FUNCS_VERSION) {
127             FreeLibrary(hVolStatus);
128             hVolStatus = NULL;
129             code = -1;
130         }
131     }
132
133     if (RDR_Initialized && RDR_Notifications) {
134         long pid;
135         thread_t phandle;
136
137         lock_InitializeMutex(&rdr_evt_lock, "rdr_evt_lock", LOCK_HIERARCHY_IGNORE);
138
139         phandle = thrd_Create((SecurityAttrib) NULL, 0,
140                                        (ThreadFunc) cm_VolStatus_DeliverNotifications,
141                                        0, 0, &pid, "cm_VolStatus_DeliverNotifications");
142         osi_assertx(phandle != NULL, "cm_VolStatus_DeliverNotifications thread creation failure");
143         thrd_CloseHandle(phandle);
144
145         rdr_q_event = thrd_CreateEvent(NULL, TRUE, TRUE, "rdr_q_event");
146         if ( GetLastError() == ERROR_ALREADY_EXISTS )
147             afsi_log("Event Object Already Exists: rdr_q_event");
148     }
149
150     osi_Log1(afsd_logp,"cm_VolStatus_Initialization 0x%x", code);
151
152     return code;
153 }
154
155 /* This function is used to unload any Volume Status Handlers
156  * that were loaded during initialization.
157  */
158 long
159 cm_VolStatus_Finalize(void)
160 {
161     osi_Log1(afsd_logp,"cm_VolStatus_Finalize handle 0x%x", hVolStatus);
162
163     if ( RDR_Initialized && RDR_Notifications ) {
164         CloseHandle(rdr_q_event);
165     }
166
167     if (hVolStatus == NULL)
168         return 0;
169
170     FreeLibrary(hVolStatus);
171     hVolStatus = NULL;
172     return 0;
173 }
174
175 /* This function notifies the Volume Status Handlers that the
176  * AFS client service has started.  If the network is started
177  * at this point we call cm_VolStatus_Network_Started().
178  */
179 long
180 cm_VolStatus_Service_Started(void)
181 {
182     long code = 0;
183
184     osi_Log1(afsd_logp,"cm_VolStatus_Service_Started handle 0x%x", hVolStatus);
185
186     if (hVolStatus == NULL)
187         return 0;
188
189     code = dll_funcs.dll_VolStatus_Service_Started();
190     if (code == 0 && smb_IsNetworkStarted())
191         code = dll_funcs.dll_VolStatus_Network_Started(cm_NetbiosName, cm_NetbiosName);
192
193     return code;
194 }
195
196 /* This function notifies the Volume Status Handlers that the
197  * AFS client service is stopping.
198  */
199 long
200 cm_VolStatus_Service_Stopped(void)
201 {
202     long code = 0;
203
204     osi_Log1(afsd_logp,"cm_VolStatus_Service_Stopped handle 0x%x", hVolStatus);
205
206     if (hVolStatus == NULL)
207         return 0;
208
209     code = dll_funcs.dll_VolStatus_Service_Stopped();
210
211     return code;
212 }
213
214
215 /* This function notifies the Volume Status Handlers that the
216  * AFS client service is accepting network requests using the
217  * specified netbios names.
218  */
219 long
220 #ifdef _WIN64
221 cm_VolStatus_Network_Started(const char * netbios32, const char * netbios64)
222 #else /* _WIN64 */
223 cm_VolStatus_Network_Started(const char * netbios32)
224 #endif /* _WIN64 */
225 {
226     long code = 0;
227
228     if (RDR_Initialized && RDR_Notifications) {
229         rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
230         evp->type = netstatus;
231         evp->netstatus_data.status = TRUE;
232
233         lock_ObtainMutex(&rdr_evt_lock);
234         osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
235         lock_ReleaseMutex(&rdr_evt_lock);
236
237         thrd_SetEvent(rdr_q_event);
238     }
239
240     if (hVolStatus == NULL)
241         return 0;
242
243 #ifdef _WIN64
244     code = dll_funcs.dll_VolStatus_Network_Started(netbios32, netbios64);
245 #else
246     code = dll_funcs.dll_VolStatus_Network_Started(netbios32, netbios32);
247 #endif
248
249     return code;
250 }
251
252 /* This function notifies the Volume Status Handlers that the
253  * AFS client service is no longer accepting network requests
254  * using the specified netbios names
255  */
256 long
257 #ifdef _WIN64
258 cm_VolStatus_Network_Stopped(const char * netbios32, const char * netbios64)
259 #else /* _WIN64 */
260 cm_VolStatus_Network_Stopped(const char * netbios32)
261 #endif /* _WIN64 */
262 {
263     long code = 0;
264
265     if (RDR_Initialized && RDR_Notifications) {
266         rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
267         evp->type = netstatus;
268         evp->netstatus_data.status = FALSE;
269
270         lock_ObtainMutex(&rdr_evt_lock);
271         osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
272         lock_ReleaseMutex(&rdr_evt_lock);
273
274         thrd_SetEvent(rdr_q_event);
275     }
276
277     if (hVolStatus == NULL)
278         return 0;
279
280 #ifdef _WIN64
281     code = dll_funcs.dll_VolStatus_Network_Stopped(netbios32, netbios64);
282 #else
283     code = dll_funcs.dll_VolStatus_Network_Stopped(netbios32, netbios32);
284 #endif
285
286     return code;
287 }
288
289 /* This function is called when the IP address list changes.
290  * Volume Status Handlers can use this notification as a hint
291  * that it might be possible to determine volume IDs for paths
292  * that previously were not accessible.
293  */
294 long
295 cm_VolStatus_Network_Addr_Change(void)
296 {
297     long code = 0;
298
299     if (RDR_Initialized && RDR_Notifications) {
300         rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
301         evp->type = addrchg;
302
303         lock_ObtainMutex(&rdr_evt_lock);
304         osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
305         lock_ReleaseMutex(&rdr_evt_lock);
306
307         thrd_SetEvent(rdr_q_event);
308     }
309
310     if (hVolStatus == NULL)
311         return 0;
312
313     code = dll_funcs.dll_VolStatus_Network_Addr_Change();
314
315     return code;
316 }
317
318 /* This function notifies the Volume Status Handlers that the
319  * state of the specified cell.volume has changed.
320  */
321 long
322 cm_VolStatus_Change_Notification(afs_uint32 cellID, afs_uint32 volID, enum volstatus status)
323 {
324     long code = 0;
325
326     if (RDR_Initialized && RDR_Notifications) {
327         rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
328         switch (status) {
329         case vl_alldown:
330         case vl_offline:
331             evp->type = volstatus;
332             evp->volstatus_data.cellID = cellID;
333             evp->volstatus_data.volID = volID;
334             evp->volstatus_data.online = FALSE;
335
336             lock_ObtainMutex(&rdr_evt_lock);
337             osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
338             lock_ReleaseMutex(&rdr_evt_lock);
339             break;
340         default:
341             evp->type = volstatus;
342             evp->volstatus_data.cellID = cellID;
343             evp->volstatus_data.volID = volID;
344             evp->volstatus_data.online = TRUE;
345
346             lock_ObtainMutex(&rdr_evt_lock);
347             osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
348             lock_ReleaseMutex(&rdr_evt_lock);
349         }
350
351         thrd_SetEvent(rdr_q_event);
352     }
353
354     if (hVolStatus == NULL)
355         return 0;
356
357     code = dll_funcs.dll_VolStatus_Change_Notification(cellID, volID, status);
358
359     return code;
360 }
361
362
363
364 long
365 cm_VolStatus_Notify_DFS_Mapping(cm_scache_t *scp, const clientchar_t *ctidPathp,
366                                 const clientchar_t *cpathp)
367 {
368     long code = 0;
369     char src[1024], *p;
370     size_t len;
371     char * tidPathp = NULL;
372     char * pathp = NULL;
373
374     if (hVolStatus == NULL || dll_funcs.version < 2)
375         return 0;
376
377     tidPathp = cm_ClientStringToUtf8Alloc(ctidPathp, -1, NULL);
378     pathp = cm_ClientStringToUtf8Alloc(cpathp, -1, NULL);
379
380     snprintf(src,sizeof(src), "\\\\%s%s", volstat_NetbiosName, tidPathp);
381     len = strlen(src);
382     if ((src[len-1] == '\\' || src[len-1] == '/') &&
383         (pathp[0] == '\\' || pathp[0] == '/'))
384         strncat(src, &pathp[1], sizeof(src));
385     else
386         strncat(src, pathp, sizeof(src));
387
388     for ( p=src; *p; p++ ) {
389         if (*p == '/')
390             *p = '\\';
391     }
392
393     code = dll_funcs.dll_VolStatus_Notify_DFS_Mapping(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
394                                                       src, scp->mountPointStringp);
395
396     if (tidPathp)
397         free(tidPathp);
398     if (pathp)
399         free(pathp);
400
401     return code;
402 }
403
404 long
405 cm_VolStatus_Invalidate_DFS_Mapping(cm_scache_t *scp)
406 {
407     long code = 0;
408
409     if (hVolStatus == NULL || dll_funcs.version < 2)
410         return 0;
411
412     code = dll_funcs.dll_VolStatus_Invalidate_DFS_Mapping(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
413
414     return code;
415 }
416
417
418 long __fastcall
419 cm_VolStatus_Path_To_ID(const char * share, const char * path, afs_uint32 * cellID, afs_uint32 * volID, enum volstatus *pstatus)
420 {
421     afs_uint32  code = 0;
422     cm_req_t    req;
423     cm_scache_t *scp;
424     cm_volume_t *volp;
425     clientchar_t * cpath = NULL;
426     clientchar_t * cshare = NULL;
427
428     if (cellID == NULL || volID == NULL)
429         return CM_ERROR_INVAL;
430
431     osi_Log2(afsd_logp,"cm_VolStatus_Path_To_ID share %s path %s",
432               osi_LogSaveString(afsd_logp, (char *)share), osi_LogSaveString(afsd_logp, (char *)path));
433
434     cm_InitReq(&req);
435
436     cpath = cm_FsStringToClientStringAlloc(path, -1, NULL);
437     cshare = cm_FsStringToClientStringAlloc(share, -1, NULL);
438
439     if (cpath == NULL || cshare == NULL) {
440         osi_Log1(afsd_logp, "Can't convert %s string. Aborting",
441                  (cpath == NULL)? "path" : "share");
442         code = CM_ERROR_NOSUCHPATH;
443         goto done;
444     }
445
446     code = cm_NameI(cm_RootSCachep(cm_rootUserp, &req), cpath,
447                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
448                     cm_rootUserp, cshare, &req, &scp);
449     if (code)
450         goto done;
451
452     lock_ObtainWrite(&scp->rw);
453     code = cm_SyncOp(scp, NULL,cm_rootUserp, &req, 0,
454                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
455     if (code) {
456         lock_ReleaseWrite(&scp->rw);
457         cm_ReleaseSCache(scp);
458         goto done;
459     }
460
461     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
462
463     *cellID = scp->fid.cell;
464     *volID  = scp->fid.volume;
465     volp = cm_GetVolumeByFID(&scp->fid);
466     if (volp) {
467         *pstatus = cm_GetVolumeStatus(volp, scp->fid.volume);
468         cm_PutVolume(volp);
469     } else
470         *pstatus = vl_unknown;
471
472     lock_ReleaseWrite(&scp->rw);
473     cm_ReleaseSCache(scp);
474
475   done:
476     if (cpath)
477         free(cpath);
478     if (cshare)
479         free(cshare);
480
481     osi_Log1(afsd_logp,"cm_VolStatus_Path_To_ID code 0x%x",code);
482     return code;
483 }
484
485 long __fastcall
486 cm_VolStatus_Path_To_DFSlink(const char * share, const char * path, afs_uint32 *pBufSize, char *pBuffer)
487 {
488     afs_uint32  code = 0;
489     cm_req_t    req;
490     cm_scache_t *scp;
491     size_t      len;
492     clientchar_t *cpath = NULL;
493     clientchar_t *cshare = NULL;
494
495     if (pBufSize == NULL || (pBuffer == NULL && *pBufSize != 0))
496         return CM_ERROR_INVAL;
497
498     osi_Log2(afsd_logp,"cm_VolStatus_Path_To_DFSlink share %s path %s",
499               osi_LogSaveString(afsd_logp, (char *)share), osi_LogSaveString(afsd_logp, (char *)path));
500
501     cm_InitReq(&req);
502
503     cpath = cm_FsStringToClientStringAlloc(path, -1, NULL);
504     cshare = cm_FsStringToClientStringAlloc(share, -1, NULL);
505
506     if (cpath == NULL || cshare == NULL) {
507         osi_Log1(afsd_logp, "Can't convert %s string. Aborting",
508                  (cpath == NULL)? "path" : "share");
509         code = CM_ERROR_NOSUCHPATH;
510         goto done;
511     }
512
513     code = cm_NameI(cm_RootSCachep(cm_rootUserp, &req), cpath, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
514                     cm_rootUserp, cshare, &req, &scp);
515     if (code)
516         goto done;
517
518     lock_ObtainWrite(&scp->rw);
519     code = cm_SyncOp(scp, NULL, cm_rootUserp, &req, 0,
520                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
521     if (code) {
522         lock_ReleaseWrite(&scp->rw);
523         cm_ReleaseSCache(scp);
524         goto done;
525     }
526
527     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
528
529     if (scp->fileType != CM_SCACHETYPE_DFSLINK) {
530         code = CM_ERROR_NOT_A_DFSLINK;
531         goto done;
532     }
533
534     len = strlen(scp->mountPointStringp) + 1;
535     if (pBuffer == NULL)
536         *pBufSize = len;
537     else if (*pBufSize >= len) {
538         strcpy(pBuffer, scp->mountPointStringp);
539         *pBufSize = len;
540     } else {
541         code = CM_ERROR_TOOBIG;
542         goto done;
543     }
544
545     lock_ReleaseWrite(&scp->rw);
546     cm_ReleaseSCache(scp);
547
548   done:
549     if (cpath)
550         free(cpath);
551     if (cshare)
552         free(cshare);
553
554     osi_Log1(afsd_logp,"cm_VolStatus_Path_To_DFSlink code 0x%x",code);
555     return code;
556 }
557
558 void
559 cm_VolStatus_DeliverNotifications(void * dummy)
560 {
561     rdr_volstat_evt_t *evp, *evprev;
562     afs_uint32 code;
563
564     while ( TRUE ) {
565         code = thrd_WaitForSingleObject_Event( rdr_q_event, INFINITE );
566
567         lock_ObtainMutex(&rdr_evt_lock);
568         for (evp = rdr_evtT; evp; evp = evprev)
569         {
570             evprev = (rdr_volstat_evt_t *) osi_QPrev(&evp->q);
571             osi_QRemoveHT((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
572             lock_ReleaseMutex(&rdr_evt_lock);
573
574             switch ( evp->type ) {
575             case addrchg:
576                 RDR_NetworkAddrChange();
577                 break;
578             case volstatus:
579                 RDR_VolumeStatus(evp->volstatus_data.cellID, evp->volstatus_data.volID, evp->volstatus_data.online);
580                 break;
581             case netstatus:
582                 RDR_NetworkStatus(evp->netstatus_data.status);
583                 break;
584             }
585
586             free(evp);
587             lock_ObtainMutex(&rdr_evt_lock);
588         }
589         lock_ReleaseMutex(&rdr_evt_lock);
590     }
591 }