4b90a048c953980145ef97d3067bd2bbc81d5afb
[openafs.git] / src / WINNT / afsclass / afsclassfn.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <winsock2.h>
11 #include <ws2tcpip.h>
12
13 extern "C" {
14 #include <afs/param.h>
15 #include <afs/stds.h>
16 }
17
18 #include <WINNT/afsclass.h>
19 #include "internal.h"
20
21
22 /*
23  * DEFINITIONS ________________________________________________________________
24  *
25  */
26
27 #define cREALLOC_ADMINLISTENTRIES   32
28
29 #define cREALLOC_HOSTLISTENTRIES    16
30
31 #define cREALLOC_SERVERKEYS         16
32
33 #define ACCOUNTACCESS_TO_USERACCESS(_aa) ( ((_aa) == aaOWNER_ONLY) ? PTS_USER_OWNER_ACCESS : PTS_USER_ANYUSER_ACCESS )
34
35 #define ACCOUNTACCESS_TO_GROUPACCESS(_aa) ( ((_aa) == aaOWNER_ONLY) ? PTS_GROUP_OWNER_ACCESS : ((_aa) == aaGROUP_ONLY) ? PTS_GROUP_ACCESS : PTS_GROUP_ANYUSER_ACCESS )
36
37
38 /*
39  * PROTOTYPES _________________________________________________________________
40  *
41  */
42
43
44 /*
45  * ROUTINES ___________________________________________________________________
46  *
47  */
48
49 BOOL AfsClass_GetServerLogFile (LPIDENT lpiServer, LPTSTR pszLocal, LPTSTR pszRemote, ULONG *pStatus)
50 {
51    BOOL rc = TRUE;
52    ULONG status;
53
54    AfsClass_Enter();
55    NOTIFYCALLBACK::SendNotificationToAll (evtGetServerLogFileBegin, lpiServer, pszRemote, 0);
56
57    PVOID hCell = NULL;
58    PVOID hBOS = NULL;
59    LPSERVER lpServer = NULL;
60    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
61       rc = FALSE;
62    else
63       {
64       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
65          rc = FALSE;
66       lpServer->Close();
67       }
68
69    if (rc)
70       {
71       WORKERPACKET wp;
72       wp.wpBosLogGet.hServer = hBOS;
73       wp.wpBosLogGet.pszLogName = pszRemote;
74       wp.wpBosLogGet.pszLogData = NULL;
75
76       AfsClass_Leave();
77       if ((rc = Worker_DoTask (wtaskBosLogGet, &wp, &status)) == TRUE)
78          {
79          HANDLE fh;
80          if ((fh = CreateFile (pszLocal, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL)) != INVALID_HANDLE_VALUE)
81             {
82             // Write the file a line at a time in order to make
83             // sure that each line ends with "\r\n".  If we encounter
84             // a line which ends in \r\n already, well, leave it alone.
85             //
86             for (LPTSTR psz = wp.wpBosLogGet.pszLogData; psz && *psz; )
87                {
88                LPTSTR pszNext;
89                for (pszNext = psz; *pszNext && (*pszNext != TEXT('\r')) && (*pszNext != TEXT('\n')); ++pszNext)
90                   ;
91                DWORD cbWrite;
92                DWORD cbWrote;
93                if ((cbWrite = (DWORD)(pszNext - psz)) != 0)
94                   WriteFile (fh, psz, cbWrite, &cbWrote, NULL);
95                WriteFile (fh, TEXT("\r\n"), 2, &cbWrote, NULL);
96                psz = (*pszNext == TEXT('\r')) ? (2+pszNext) : (*pszNext == TEXT('\n')) ? (1+pszNext) : NULL;
97                }
98             CloseHandle (fh);
99             }
100
101          Free (wp.wpBosLogGet.pszLogData);
102          }
103
104       AfsClass_Enter();
105       }
106
107    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
108       {
109       lpServer->CloseBosObject();
110       lpServer->Close();
111       }
112
113    NOTIFYCALLBACK::SendNotificationToAll (evtGetServerLogFileEnd, lpiServer, pszRemote, status);
114    AfsClass_Leave();
115
116    if (pStatus && !rc)
117       *pStatus = status;
118    return rc;
119 }
120
121
122 BOOL AfsClass_SetServerAuth (LPIDENT lpiServer, BOOL fEnabled, ULONG *pStatus)
123 {
124    BOOL rc = TRUE;
125    ULONG status;
126
127    AfsClass_Enter();
128    NOTIFYCALLBACK::SendNotificationToAll (evtSetServerAuthBegin, lpiServer);
129
130    PVOID hCell;
131    PVOID hBOS;
132    LPSERVER lpServer;
133    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
134       rc = FALSE;
135    else
136       {
137       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
138          rc = FALSE;
139       lpServer->Close();
140       }
141
142    if (rc)
143       {
144       WORKERPACKET wp;
145       wp.wpBosAuthSet.hServer = hBOS;
146       wp.wpBosAuthSet.fEnableAuth = fEnabled;
147
148       AfsClass_Leave();
149       rc = Worker_DoTask (wtaskBosAuthSet, &wp, &status);
150       AfsClass_Enter();
151       }
152
153    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
154       {
155       lpServer->CloseBosObject();
156       lpServer->Close();
157       }
158
159    NOTIFYCALLBACK::SendNotificationToAll (evtSetServerAuthEnd, lpiServer, status);
160    AfsClass_Leave();
161
162    if (pStatus && !rc)
163       *pStatus = status;
164    return rc;
165 }
166
167
168 BOOL AfsClass_StartService (LPIDENT lpiStart, BOOL fTemporary, ULONG *pStatus)
169 {
170    BOOL rc = TRUE;
171    ULONG status;
172
173    AfsClass_Enter();
174    NOTIFYCALLBACK::SendNotificationToAll (evtStartServiceBegin, lpiStart);
175
176    PVOID hCell;
177    PVOID hBOS;
178    LPSERVER lpServer;
179    if ((lpServer = lpiStart->OpenServer (&status)) == NULL)
180       rc = FALSE;
181    else
182       {
183       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
184          rc = FALSE;
185       lpServer->Close();
186       }
187
188    if (rc)
189       {
190       if (lpiStart->fIsService())
191          {
192          TCHAR szName[ cchNAME ];
193          lpiStart->GetServiceName (szName);
194
195          if (fTemporary)
196             {
197             WORKERPACKET wp;
198             wp.wpBosProcessExecutionStateSetTemporary.hServer = hBOS;
199             wp.wpBosProcessExecutionStateSetTemporary.pszService = szName;
200             wp.wpBosProcessExecutionStateSetTemporary.state = SERVICESTATE_RUNNING;
201
202             AfsClass_Leave();
203             rc = Worker_DoTask (wtaskBosProcessExecutionStateSetTemporary, &wp, &status);
204             AfsClass_Enter();
205             }
206          else
207             {
208             WORKERPACKET wp;
209             wp.wpBosProcessExecutionStateSet.hServer = hBOS;
210             wp.wpBosProcessExecutionStateSet.pszService = szName;
211             wp.wpBosProcessExecutionStateSet.state = SERVICESTATE_RUNNING;
212
213             AfsClass_Leave();
214             rc = Worker_DoTask (wtaskBosProcessExecutionStateSet, &wp, &status);
215             AfsClass_Enter();
216             }
217          }
218       else
219          {
220          WORKERPACKET wp;
221          wp.wpBosProcessAllStart.hServer = hBOS;
222          AfsClass_Leave();
223          rc = Worker_DoTask (wtaskBosProcessAllStart, &wp, &status);
224          AfsClass_Enter();
225          }
226       }
227
228    if (rc)
229       {
230       if (lpiStart->fIsService())
231          {
232          LPSERVICE lpService;
233          if ((lpService = lpiStart->OpenService (&status)) == NULL)
234             rc = FALSE;
235          else
236             {
237             lpService->Invalidate();
238             lpService->RefreshStatus();
239             lpService->Close();
240             }
241          }
242       else
243          {
244          if ((lpServer = lpiStart->OpenServer (&status)) == NULL)
245             rc = FALSE;
246          else
247             {
248             lpServer->Invalidate();
249             lpServer->RefreshAll();
250             lpServer->Close();
251             }
252          }
253       }
254
255    if ((lpServer = lpiStart->OpenServer (&status)) != NULL)
256       {
257       lpServer->CloseBosObject();
258       lpServer->Close();
259       }
260
261    NOTIFYCALLBACK::SendNotificationToAll (evtStartServiceEnd, lpiStart, status);
262    AfsClass_Leave();
263
264    if (pStatus && !rc)
265       *pStatus = status;
266    return rc;
267 }
268
269
270 BOOL AfsClass_StopService (LPIDENT lpiStop, BOOL fTemporary, BOOL fWait, ULONG *pStatus)
271 {
272    BOOL rc = TRUE;
273    ULONG status;
274
275    AfsClass_Enter();
276    NOTIFYCALLBACK::SendNotificationToAll (evtStopServiceBegin, lpiStop);
277
278    PVOID hCell;
279    PVOID hBOS;
280    LPSERVER lpServer;
281    if ((lpServer = lpiStop->OpenServer (&status)) == NULL)
282       rc = FALSE;
283    else
284       {
285       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
286          rc = FALSE;
287       lpServer->Close();
288       }
289
290    if (rc)
291       {
292       if (lpiStop->fIsService())
293          {
294          TCHAR szName[ cchNAME ];
295          lpiStop->GetServiceName (szName);
296
297          if (fTemporary)
298             {
299             WORKERPACKET wp;
300             wp.wpBosProcessExecutionStateSetTemporary.hServer = hBOS;
301             wp.wpBosProcessExecutionStateSetTemporary.pszService = szName;
302             wp.wpBosProcessExecutionStateSetTemporary.state = SERVICESTATE_STOPPED;
303             // TODO: wp.wpStopBosProcessTemporary.fWait = TRUE;
304
305             AfsClass_Leave();
306             rc = Worker_DoTask (wtaskBosProcessExecutionStateSetTemporary, &wp, &status);
307             AfsClass_Enter();
308             }
309          else
310             {
311             WORKERPACKET wp;
312             wp.wpBosProcessExecutionStateSet.hServer = hBOS;
313             wp.wpBosProcessExecutionStateSet.pszService = szName;
314             wp.wpBosProcessExecutionStateSet.state = SERVICESTATE_STOPPED;
315             // TODO: wp.wpStopBosProcess.fWait = TRUE;
316
317             AfsClass_Leave();
318             rc = Worker_DoTask (wtaskBosProcessExecutionStateSet, &wp, &status);
319             AfsClass_Enter();
320             }
321          }
322       else if (fWait)
323          {
324          WORKERPACKET wp;
325          wp.wpBosProcessAllWaitStop.hServer = hBOS;
326
327          AfsClass_Leave();
328          rc = Worker_DoTask (wtaskBosProcessAllWaitStop, &wp, &status);
329          AfsClass_Enter();
330          }
331       else // (!fWait)
332          {
333          WORKERPACKET wp;
334          wp.wpBosProcessAllStop.hServer = hBOS;
335
336          AfsClass_Leave();
337          rc = Worker_DoTask (wtaskBosProcessAllStop, &wp, &status);
338          AfsClass_Enter();
339          }
340       }
341
342    if (rc)
343       {
344       if (lpiStop->fIsService())
345          {
346          LPSERVICE lpService;
347          if ((lpService = lpiStop->OpenService (&status)) == NULL)
348             rc = FALSE;
349          else
350             {
351             lpService->Invalidate();
352             lpService->RefreshStatus();
353             lpService->Close();
354             }
355          }
356       else
357          {
358          LPSERVER lpServer;
359          if ((lpServer = lpiStop->OpenServer (&status)) == NULL)
360             rc = FALSE;
361          else
362             {
363             lpServer->Invalidate();
364             lpServer->RefreshAll();
365             lpServer->Close();
366             }
367          }
368       }
369
370    if ((lpServer = lpiStop->OpenServer (&status)) != NULL)
371       {
372       lpServer->CloseBosObject();
373       lpServer->Close();
374       }
375
376    NOTIFYCALLBACK::SendNotificationToAll (evtStopServiceEnd, lpiStop, status);
377    AfsClass_Leave();
378
379    if (pStatus && !rc)
380       *pStatus = status;
381    return rc;
382 }
383
384
385 BOOL AfsClass_RestartService (LPIDENT lpiRestart, ULONG *pStatus)
386 {
387    BOOL rc = TRUE;
388    ULONG status;
389
390    AfsClass_Enter();
391    NOTIFYCALLBACK::SendNotificationToAll (evtRestartServiceBegin, lpiRestart);
392
393    PVOID hCell = NULL;
394    PVOID hBOS = NULL;
395    LPSERVER lpServer;
396    if ((lpServer = lpiRestart->OpenServer (&status)) == NULL)
397       rc = FALSE;
398    else
399       {
400       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
401          rc = FALSE;
402       lpServer->Close();
403       }
404
405    BOOL fRestartAll = FALSE;
406    if (!lpiRestart->fIsService())
407       fRestartAll = TRUE;
408
409    TCHAR szServiceRestart[ cchNAME ];
410    if (lpiRestart->fIsService())
411       {
412       lpiRestart->GetServiceName (szServiceRestart);
413       if (!lstrcmpi (szServiceRestart, TEXT("BOS")))
414          fRestartAll = TRUE;
415       }
416
417    if (rc)
418       {
419       if (!fRestartAll)
420          {
421          WORKERPACKET wp;
422          wp.wpBosProcessRestart.hServer = hBOS;
423          wp.wpBosProcessRestart.pszService = szServiceRestart;
424
425          AfsClass_Leave();
426          rc = Worker_DoTask (wtaskBosProcessRestart, &wp, &status);
427          AfsClass_Enter();
428          }
429       else // (fRestartAll)
430          {
431          WORKERPACKET wp;
432          wp.wpBosProcessAllStopAndRestart.hServer = hBOS;
433          wp.wpBosProcessAllStopAndRestart.fRestartBOS = TRUE;
434
435          AfsClass_Leave();
436          rc = Worker_DoTask (wtaskBosProcessAllStopAndRestart, &wp, &status);
437          AfsClass_Enter();
438          }
439       }
440
441    if (rc)
442       {
443       if (!fRestartAll)
444          {
445          LPSERVICE lpService;
446          if ((lpService = lpiRestart->OpenService (&status)) == NULL)
447             rc = FALSE;
448          else
449             {
450             lpService->Invalidate();
451             lpService->RefreshStatus();
452             lpService->Close();
453             }
454          }
455       else // (fRestartAll)
456          {
457          LPSERVER lpServer;
458          if ((lpServer = lpiRestart->OpenServer (&status)) == NULL)
459             rc = FALSE;
460          else
461             {
462             lpServer->Invalidate();
463             lpServer->RefreshAll();
464             lpServer->Close();
465             }
466          }
467       }
468
469    if ((lpServer = lpiRestart->OpenServer (&status)) != NULL)
470       {
471       lpServer->CloseBosObject();
472       lpServer->Close();
473       }
474
475    NOTIFYCALLBACK::SendNotificationToAll (evtRestartServiceEnd, lpiRestart, status);
476    AfsClass_Leave();
477
478    if (pStatus && !rc)
479       *pStatus = status;
480    return rc;
481 }
482
483
484 LPIDENT AfsClass_CreateFileset (LPIDENT lpiAggregate, LPTSTR pszFileset, ULONG ckQuota, ULONG *pStatus)
485 {
486    LPIDENT lpiFileset = NULL;
487    ULONG status;
488    BOOL rc = TRUE;
489
490    AfsClass_Enter();
491    NOTIFYCALLBACK::SendNotificationToAll (evtCreateFilesetBegin, lpiAggregate, pszFileset, 0);
492
493    // Obtain hCell and hVOS
494    //
495    PVOID hCell = NULL;
496    PVOID hVOS = NULL;
497    LPSERVER lpServer = NULL;
498    if ((lpServer = lpiAggregate->OpenServer (&status)) == NULL)
499       rc = FALSE;
500    else
501       {
502       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
503          rc = FALSE;
504       lpServer->Close();
505       }
506
507    // Obtain idPartition
508    //
509    int idPartition;
510    LPAGGREGATE lpAggregate;
511    if ((lpAggregate = lpiAggregate->OpenAggregate (&status)) == NULL)
512       rc = FALSE;
513    else
514       {
515       idPartition = lpAggregate->GetID();
516       lpAggregate->Close();
517       }
518
519    // Perform the actual operation
520    //
521    if (rc)
522       {
523       WORKERPACKET wp;
524       wp.wpVosVolumeCreate.hCell = hCell;
525       wp.wpVosVolumeCreate.hServer = hVOS;
526       wp.wpVosVolumeCreate.idPartition = idPartition;
527       wp.wpVosVolumeCreate.pszVolume = pszFileset;
528       wp.wpVosVolumeCreate.ckQuota = ckQuota;
529
530       AfsClass_Leave();
531       rc = Worker_DoTask (wtaskVosVolumeCreate, &wp, &status);
532       AfsClass_Enter();
533       }
534
535    // Clean up
536    //
537    if (rc)
538       {
539       LPAGGREGATE lpAggregate;
540       if ((lpAggregate = lpiAggregate->OpenAggregate (&status)) == NULL)
541          rc = FALSE;
542       else
543          {
544          lpAggregate->Invalidate();
545          lpAggregate->RefreshFilesets (TRUE, &status);
546          lpAggregate->Close();
547          }
548       }
549
550    if (rc)
551       {
552       LPCELL lpCell;
553       if ((lpCell = lpiAggregate->OpenCell()) == NULL)
554          rc = FALSE;
555       else
556          {
557          lpCell->RefreshVLDB (lpiAggregate);
558          lpCell->Close();
559          }
560       }
561
562    if (rc)
563       {
564       LPAGGREGATE lpAggregate;
565       if ((lpAggregate = lpiAggregate->OpenAggregate (&status)) == NULL)
566          rc = FALSE;
567       else
568          {
569          LPFILESET lpFileset;
570          if ((lpFileset = lpAggregate->OpenFileset (pszFileset, &status)) == NULL)
571             rc = FALSE;
572          else
573             {
574             lpiFileset = lpFileset->GetIdentifier();
575             lpFileset->Close();
576             }
577          lpAggregate->Close();
578          }
579       }
580
581    if (hVOS)
582       {
583       if ((lpServer = lpiAggregate->OpenServer (&status)) != NULL)
584          {
585          lpServer->CloseVosObject();
586          lpServer->Close();
587          }
588       }
589
590    NOTIFYCALLBACK::SendNotificationToAll (evtCreateFilesetEnd, lpiAggregate, pszFileset, status);
591    AfsClass_Leave();
592
593    if (pStatus && !rc)
594       *pStatus = status;
595    return (rc) ? lpiFileset : NULL;
596 }
597
598
599 BOOL AfsClass_DeleteFileset (LPIDENT lpiFileset, BOOL fVLDB, BOOL fServer, ULONG *pStatus)
600 {
601    BOOL rc = TRUE;
602    ULONG status;
603
604    AfsClass_Enter();
605    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteFilesetBegin, lpiFileset);
606
607    // Obtain hCell and hVOS
608    //
609    PVOID hCell = NULL;
610    PVOID hVOS = NULL;
611    LPSERVER lpServer = NULL;
612    if ((lpServer = lpiFileset->OpenServer (&status)) == NULL)
613       rc = FALSE;
614    else
615       {
616       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
617          rc = FALSE;
618       lpServer->Close();
619       }
620
621    // Does the fileset have a VLDB entry? Does it actually exist on the server?
622    // What's its volume ID?  Its R/W ID?  Its partition ID?
623    //
624    VOLUMEID vidFileset;
625    VOLUMEID vidReadWrite;
626    int wFilesetGhost;
627
628    // Obtain the ID of the fileset's parent aggregate.
629    //
630    int idPartition;
631    LPAGGREGATE lpAggregate;
632    if ((lpAggregate = lpiFileset->OpenAggregate (&status)) == NULL)
633       rc = FALSE;
634    else
635       {
636       if ((idPartition = lpAggregate->GetID()) == NO_PARTITION)
637          rc = FALSE;
638       lpAggregate->Close();
639       }
640
641    if (rc)
642       {
643       LPFILESET lpFileset;
644       if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
645          rc = FALSE;
646       else
647          {
648          wFilesetGhost = lpFileset->GetGhostStatus();
649          lpiFileset->GetFilesetID (&vidFileset);
650
651          FILESETSTATUS fs;
652          if (!lpFileset->GetStatus (&fs))
653             vidReadWrite = vidFileset;
654          else
655             vidReadWrite = fs.idReadWrite;
656
657          lpFileset->Close();
658          }
659       }
660
661    if (!(wFilesetGhost & GHOST_HAS_VLDB_ENTRY))
662       fVLDB = FALSE;
663    if (!(wFilesetGhost & GHOST_HAS_SERVER_ENTRY))
664       fServer = FALSE;
665
666    if (rc && fVLDB && fServer)
667       {
668       WORKERPACKET wp;
669       wp.wpVosVolumeDelete.hCell = hCell;
670       wp.wpVosVolumeDelete.hServer = hVOS;
671       wp.wpVosVolumeDelete.idPartition = idPartition;
672       wp.wpVosVolumeDelete.idVolume = vidFileset;
673
674       AfsClass_Leave();
675       rc = Worker_DoTask (wtaskVosVolumeDelete, &wp, &status);
676       AfsClass_Enter();
677       }
678    else if (rc && fVLDB)
679       {
680       WORKERPACKET wp;
681       wp.wpVosVLDBEntryRemove.hCell = hCell;
682       wp.wpVosVLDBEntryRemove.hServer = hVOS;
683       wp.wpVosVLDBEntryRemove.idPartition = idPartition;
684       wp.wpVosVLDBEntryRemove.idVolume = vidReadWrite;
685
686       AfsClass_Leave();
687       rc = Worker_DoTask (wtaskVosVLDBEntryRemove, &wp, &status);
688       AfsClass_Enter();
689       }
690    else if (rc && fServer)
691       {
692       WORKERPACKET wp;
693       wp.wpVosVolumeZap.hCell = hCell;
694       wp.wpVosVolumeZap.hServer = hVOS;
695       wp.wpVosVolumeZap.idPartition = idPartition;
696       wp.wpVosVolumeZap.idVolume = vidFileset;
697       wp.wpVosVolumeZap.fForce = TRUE;
698
699       AfsClass_Leave();
700       rc = Worker_DoTask (wtaskVosVolumeZap, &wp, &status);
701       AfsClass_Enter();
702       }
703
704    if (rc)
705       {
706       LPAGGREGATE lpAggregate;
707       if ((lpAggregate = lpiFileset->OpenAggregate (&status)) == NULL)
708          rc = FALSE;
709       else
710          {
711          lpAggregate->Invalidate();
712          lpAggregate->RefreshFilesets (TRUE);
713          lpAggregate->Close();
714          }
715       }
716
717    if (rc)
718       {
719       LPCELL lpCell;
720       if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
721          rc = FALSE;
722       else
723          {
724          lpCell->RefreshVLDB (lpiFileset->GetAggregate(), TRUE);
725          lpCell->Close();
726          }
727       }
728
729    if (hVOS)
730       {
731       if ((lpServer = lpiFileset->OpenServer()) != NULL)
732          {
733          lpServer->CloseVosObject();
734          lpServer->Close();
735          }
736       }
737
738    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteFilesetEnd, lpiFileset, status);
739    AfsClass_Leave();
740
741    if (pStatus && !rc)
742       *pStatus = status;
743    return rc;
744 }
745
746
747 BOOL AfsClass_MoveFileset (LPIDENT lpiFileset, LPIDENT lpiAggregateTarget, ULONG *pStatus)
748 {
749    BOOL rc = TRUE;
750    ULONG status;
751
752    AfsClass_Enter();
753    NOTIFYCALLBACK::SendNotificationToAll (evtMoveFilesetBegin, lpiFileset, lpiAggregateTarget, NULL, NULL, 0, 0);
754
755    LPIDENT lpiAggregateSource = lpiFileset->GetAggregate();
756
757    // Obtain hCell, hVOS and the aggregate name for the source
758    //
759    PVOID hCell;
760    PVOID hVOSSource = NULL;
761    LPSERVER lpServer;
762    if ((lpServer = lpiFileset->OpenServer (&status)) == NULL)
763       rc = FALSE;
764    else
765       {
766       if ((hVOSSource = lpServer->OpenVosObject (&hCell, &status)) == NULL)
767          rc = FALSE;
768       lpServer->Close();
769       }
770
771    // Obtain the ID of the source aggregate
772    //
773    int idPartitionSource;
774    LPAGGREGATE lpAggregate;
775    if ((lpAggregate = lpiFileset->OpenAggregate (&status)) == NULL)
776       rc = FALSE;
777    else
778       {
779       if ((idPartitionSource = lpAggregate->GetID()) == NO_PARTITION)
780          rc = FALSE;
781       lpAggregate->Close();
782       }
783
784    // Obtain hCell, hVOS and the aggregate name for the target
785    //
786    PVOID hVOSTarget = NULL;
787    if ((lpServer = lpiAggregateTarget->OpenServer (&status)) == NULL)
788       rc = FALSE;
789    else
790       {
791       if ((hVOSTarget = lpServer->OpenVosObject (NULL, &status)) == NULL)
792          rc = FALSE;
793       lpServer->Close();
794       }
795
796    // Obtain the ID of the target aggregate
797    //
798    int idPartitionTarget;
799    if ((lpAggregate = lpiAggregateTarget->OpenAggregate (&status)) == NULL)
800       rc = FALSE;
801    else
802       {
803       if ((idPartitionTarget = lpAggregate->GetID()) == NO_PARTITION)
804          rc = FALSE;
805       lpAggregate->Close();
806       }
807
808    if (rc)
809       {
810       WORKERPACKET wp;
811       wp.wpVosVolumeMove.hCell = hCell;
812       wp.wpVosVolumeMove.hServerFrom = hVOSSource;
813       wp.wpVosVolumeMove.idPartitionFrom = idPartitionSource;
814       wp.wpVosVolumeMove.hServerTo = hVOSTarget;
815       wp.wpVosVolumeMove.idPartitionTo = idPartitionTarget;
816       lpiFileset->GetFilesetID (&wp.wpVosVolumeMove.idVolume);
817
818       AfsClass_Leave();
819       rc = Worker_DoTask (wtaskVosVolumeMove, &wp, &status);
820       AfsClass_Enter();
821       }
822
823    if (rc)
824       {
825       LPAGGREGATE lpAggregate;
826       if ((lpAggregate = lpiAggregateSource->OpenAggregate (&status)) == NULL)
827          rc = FALSE;
828       else
829          {
830          lpAggregate->Invalidate();
831          lpAggregate->RefreshFilesets();
832          lpAggregate->Close();
833          }
834       }
835
836    if (rc)
837       {
838       LPAGGREGATE lpAggregate;
839       if ((lpAggregate = lpiAggregateTarget->OpenAggregate (&status)) == NULL)
840          rc = FALSE;
841       else
842          {
843          lpAggregate->Invalidate();
844          lpAggregate->RefreshFilesets();
845          lpAggregate->Close();
846          }
847       }
848
849    if (rc)
850       {
851       LPFILESET lpFileset;
852       if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
853          rc = FALSE;
854       else
855          {
856          lpFileset->Invalidate();
857          lpFileset->RefreshStatus();
858          lpFileset->Close();
859          }
860       }
861
862    if (rc)
863       {
864       LPCELL lpCell;
865       if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
866          rc = FALSE;
867       else
868          {
869          lpCell->RefreshVLDB (lpiAggregateSource, TRUE);
870          lpCell->RefreshVLDB (lpiAggregateTarget, TRUE);
871          lpCell->Close();
872          }
873       }
874
875    if (hVOSSource)
876       {
877       if ((lpServer = lpiAggregateSource->OpenServer()) != NULL)
878          {
879          lpServer->CloseVosObject();
880          lpServer->Close();
881          }
882       }
883    if (hVOSTarget)
884       {
885       if ((lpServer = lpiAggregateTarget->OpenServer()) != NULL)
886          {
887          lpServer->CloseVosObject();
888          lpServer->Close();
889          }
890       }
891
892    NOTIFYCALLBACK::SendNotificationToAll (evtMoveFilesetEnd, lpiFileset, lpiAggregateTarget, NULL, NULL, 0, status);
893    AfsClass_Leave();
894
895    if (pStatus && !rc)
896       *pStatus = status;
897    return rc;
898 }
899
900
901 BOOL AfsClass_SetFilesetQuota (LPIDENT lpiFileset, size_t ckQuotaNew, ULONG *pStatus)
902 {
903    BOOL rc = TRUE;
904    ULONG status;
905
906    AfsClass_Enter();
907    NOTIFYCALLBACK::SendNotificationToAll (evtSetFilesetQuotaBegin, lpiFileset);
908
909    // Obtain hCell and hVOS for the server where this fileset lives
910    //
911    PVOID hCell;
912    PVOID hVOS = NULL;
913    LPSERVER lpServer;
914    if ((lpServer = lpiFileset->OpenServer (&status)) == NULL)
915       rc = FALSE;
916    else
917       {
918       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
919          rc = FALSE;
920       lpServer->Close();
921       }
922
923    // Obtain the ID of the fileset's parent aggregate.
924    //
925    int idPartition;
926    if (rc)
927       {
928       LPAGGREGATE lpAggregate;
929       if ((lpAggregate = lpiFileset->OpenAggregate (&status)) == NULL)
930          rc = FALSE;
931       else
932          {
933          if ((idPartition = lpAggregate->GetID()) == NO_PARTITION)
934             rc = FALSE;
935          lpAggregate->Close();
936          }
937       }
938
939    // Change the fileset's quota.
940    //
941    if (rc)
942       {
943       WORKERPACKET wp;
944       wp.wpVosVolumeQuotaChange.hCell = hCell;
945       wp.wpVosVolumeQuotaChange.hServer = hVOS;
946       wp.wpVosVolumeQuotaChange.idPartition = idPartition;
947       lpiFileset->GetFilesetID (&wp.wpVosVolumeQuotaChange.idVolume);
948       wp.wpVosVolumeQuotaChange.ckQuota = ckQuotaNew;
949
950       AfsClass_Leave();
951       rc = Worker_DoTask (wtaskVosVolumeQuotaChange, &wp, &status);
952       AfsClass_Enter();
953       }
954
955    if (rc)
956       {
957       LPFILESET lpFileset;
958       if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
959          rc = FALSE;
960       else
961          {
962          lpFileset->Invalidate();
963          lpFileset->RefreshStatus();
964          lpFileset->Close();
965          }
966       }
967
968    if (rc)
969       {
970       LPAGGREGATE lpAggregate;
971       if ((lpAggregate = lpiFileset->OpenAggregate (&status)) == NULL)
972          rc = FALSE;
973       else
974          {
975          lpAggregate->RefreshStatus();
976          lpAggregate->Close();
977          }
978       }
979
980    if (hVOS != NULL)
981       {
982       if ((lpServer = lpiFileset->OpenServer()) != NULL)
983          {
984          lpServer->CloseVosObject();
985          lpServer->Close();
986          }
987       }
988
989    NOTIFYCALLBACK::SendNotificationToAll (evtSetFilesetQuotaEnd, lpiFileset, status);
990    AfsClass_Leave();
991
992    if (pStatus && !rc)
993       *pStatus = status;
994    return rc;
995 }
996
997
998 BOOL AfsClass_SyncVLDB (LPIDENT lpiSync, BOOL fForce, ULONG *pStatus)
999 {
1000    BOOL rc = TRUE;
1001    ULONG status;
1002
1003    AfsClass_Enter();
1004    NOTIFYCALLBACK::SendNotificationToAll (evtSyncVLDBBegin, lpiSync);
1005
1006    // Obtain hCell and hVOS
1007    //
1008    PVOID hCell;
1009    PVOID hVOS = NULL;
1010    LPSERVER lpServer;
1011    if ((lpServer = lpiSync->OpenServer (&status)) == NULL)
1012       rc = FALSE;
1013    else
1014       {
1015       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
1016          rc = FALSE;
1017       lpServer->Close();
1018       }
1019
1020    // Obtain the ID of the target aggregate.
1021    //
1022    int idPartition = NO_PARTITION;
1023    if (rc && (lpiSync->fIsAggregate() || lpiSync->fIsFileset()))
1024       {
1025       LPAGGREGATE lpAggregate;
1026       if ((lpAggregate = lpiSync->OpenAggregate (&status)) == NULL)
1027          rc = FALSE;
1028       else
1029          {
1030          if ((idPartition = lpAggregate->GetID()) == NO_PARTITION)
1031             rc = FALSE;
1032          lpAggregate->Close();
1033          }
1034       }
1035
1036    if (rc)
1037       {
1038       WORKERPACKET wp;
1039       wp.wpVosVLDBSync.hCell = hCell;
1040       wp.wpVosVLDBSync.hServer = hVOS;
1041       wp.wpVosVLDBSync.idPartition = idPartition;
1042       wp.wpVosVLDBSync.fForce = fForce;
1043
1044       AfsClass_Leave();
1045       rc = Worker_DoTask (wtaskVosVLDBSync, &wp, &status);
1046       AfsClass_Enter();
1047       }
1048
1049    if (rc)
1050       {
1051       if (lpiSync->fIsServer())
1052          {
1053          LPSERVER lpServer;
1054          if ((lpServer = lpiSync->OpenServer (&status)) == NULL)
1055             rc = FALSE;
1056          else
1057             {
1058             lpServer->Invalidate();
1059             rc = lpServer->RefreshAll (&status);
1060             lpServer->Close();
1061             }
1062          }
1063       else // (lpiSync->fIsAggregate())
1064          {
1065          LPAGGREGATE lpAggregate;
1066          if ((lpAggregate = lpiSync->OpenAggregate (&status)) == NULL)
1067             rc = FALSE;
1068          else
1069             {
1070             lpAggregate->Invalidate();
1071             lpAggregate->RefreshStatus();
1072             lpAggregate->RefreshFilesets();
1073             lpAggregate->Close();
1074
1075             LPCELL lpCell;
1076             if ((lpCell = lpiSync->OpenCell()) == NULL)
1077                rc = FALSE;
1078             else
1079                {
1080                lpCell->RefreshVLDB (lpiSync);
1081                lpCell->Close();
1082                }
1083             }
1084          }
1085       }
1086
1087    if (hVOS)
1088       {
1089       if ((lpServer = lpiSync->OpenServer()) != NULL)
1090          {
1091          lpServer->CloseVosObject();
1092          lpServer->Close();
1093          }
1094       }
1095
1096    NOTIFYCALLBACK::SendNotificationToAll (evtSyncVLDBEnd, lpiSync, status);
1097    AfsClass_Leave();
1098
1099    if (pStatus && !rc)
1100       *pStatus = status;
1101    return rc;
1102 }
1103
1104
1105 BOOL AfsClass_ChangeAddress (LPIDENT lpiServer, LPSOCKADDR_IN pAddrOld, LPSOCKADDR_IN pAddrNew, ULONG *pStatus)
1106 {
1107    BOOL rc = TRUE;
1108    ULONG status;
1109
1110    AfsClass_Enter();
1111    NOTIFYCALLBACK::SendNotificationToAll (evtChangeAddressBegin, lpiServer);
1112
1113    // Obtain hCell
1114    //
1115    PVOID hCell;
1116    LPCELL lpCell;
1117    if ((lpCell = lpiServer->OpenCell (&status)) == NULL)
1118       rc = FALSE;
1119    else
1120       {
1121       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1122          rc = FALSE;
1123       lpCell->Close();
1124       }
1125
1126    if (rc && pAddrNew)
1127       {
1128       WORKERPACKET wp;
1129       wp.wpVosFileServerAddressChange.hCell = hCell;
1130       wp.wpVosFileServerAddressChange.addrOld = *pAddrOld;
1131       wp.wpVosFileServerAddressChange.addrNew = *pAddrNew;
1132
1133       AfsClass_Leave();
1134       rc = Worker_DoTask (wtaskVosFileServerAddressChange, &wp, &status);
1135       AfsClass_Enter();
1136       }
1137    else if (rc && !pAddrNew)
1138       {
1139       WORKERPACKET wp;
1140       wp.wpVosFileServerAddressRemove.hCell = hCell;
1141       wp.wpVosFileServerAddressRemove.addr = *pAddrOld;
1142
1143       AfsClass_Leave();
1144       rc = Worker_DoTask (wtaskVosFileServerAddressRemove, &wp, &status);
1145       AfsClass_Enter();
1146       }
1147
1148    if (rc)
1149       {
1150       LPSERVER lpServer;
1151       if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
1152          {
1153          lpServer->InvalidateStatus();
1154          lpServer->Close();
1155          }
1156
1157       if ((lpCell = lpiServer->OpenCell (&status)) == NULL)
1158          rc = FALSE;
1159       else
1160          {
1161          lpCell->InvalidateServers ();
1162          rc = lpCell->RefreshServers (TRUE, &status);
1163          lpCell->Close();
1164          }
1165       }
1166
1167    NOTIFYCALLBACK::SendNotificationToAll (evtChangeAddressEnd, lpiServer, status);
1168    AfsClass_Leave();
1169
1170    if (pStatus && !rc)
1171       *pStatus = status;
1172    return rc;
1173 }
1174
1175
1176 BOOL AfsClass_ChangeAddress (LPIDENT lpiServer, LPSERVERSTATUS pStatusOld, LPSERVERSTATUS pStatusNew, ULONG *pStatus)
1177 {
1178    BOOL rc = TRUE;
1179    ULONG status;
1180
1181    AfsClass_Enter();
1182    NOTIFYCALLBACK::SendNotificationToAll (evtChangeAddressBegin, lpiServer);
1183
1184    // Obtain hCell
1185    //
1186    PVOID hCell;
1187    LPCELL lpCell;
1188    if ((lpCell = lpiServer->OpenCell (&status)) == NULL)
1189       rc = FALSE;
1190    else
1191       {
1192       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1193          rc = FALSE;
1194       lpCell->Close();
1195       }
1196
1197    if (rc)
1198       {
1199       AfsClass_Leave();
1200
1201       for (size_t iAddr = 0; rc && (iAddr < AFSCLASS_MAX_ADDRESSES_PER_SITE); ++iAddr)
1202          {
1203          int oldAddress;
1204          int newAddress;
1205          AfsClass_AddressToInt (&oldAddress, &pStatusOld->aAddresses[ iAddr ]);
1206          AfsClass_AddressToInt (&newAddress, &pStatusNew->aAddresses[ iAddr ]);
1207
1208          if (oldAddress && newAddress && (oldAddress != newAddress))
1209             {
1210             WORKERPACKET wp;
1211             wp.wpVosFileServerAddressChange.hCell = hCell;
1212             wp.wpVosFileServerAddressChange.addrOld = pStatusOld->aAddresses[ iAddr ];
1213             wp.wpVosFileServerAddressChange.addrNew = pStatusNew->aAddresses[ iAddr ];
1214
1215             rc = Worker_DoTask (wtaskVosFileServerAddressChange, &wp, &status);
1216             }
1217          else if (oldAddress && !newAddress)
1218             {
1219             WORKERPACKET wp;
1220             wp.wpVosFileServerAddressRemove.hCell = hCell;
1221             wp.wpVosFileServerAddressRemove.addr = pStatusOld->aAddresses[ iAddr ];
1222
1223             rc = Worker_DoTask (wtaskVosFileServerAddressRemove, &wp, &status);
1224             }
1225          }
1226
1227       AfsClass_Enter();
1228       }
1229
1230    if (rc)
1231       {
1232       LPSERVER lpServer;
1233       if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
1234          {
1235          lpServer->InvalidateStatus();
1236          lpServer->Close();
1237          }
1238
1239       LPCELL lpCell;
1240       if ((lpCell = lpiServer->OpenCell (&status)) == NULL)
1241          rc = FALSE;
1242       else
1243          {
1244          lpCell->InvalidateServers ();
1245          rc = lpCell->RefreshServers (TRUE, &status);
1246          lpCell->Close();
1247          }
1248       }
1249
1250    NOTIFYCALLBACK::SendNotificationToAll (evtChangeAddressEnd, lpiServer, status);
1251    AfsClass_Leave();
1252
1253    if (pStatus && !rc)
1254       *pStatus = status;
1255    return rc;
1256 }
1257
1258
1259 BOOL AfsClass_LockFileset (LPIDENT lpiFileset, ULONG *pStatus)
1260 {
1261    BOOL rc = TRUE;
1262    ULONG status;
1263
1264    AfsClass_Enter();
1265    NOTIFYCALLBACK::SendNotificationToAll (evtLockFilesetBegin, lpiFileset);
1266
1267    // Obtain hCell
1268    //
1269    PVOID hCell;
1270    LPCELL lpCell;
1271    if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1272       rc = FALSE;
1273    else
1274       {
1275       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1276          rc = FALSE;
1277       lpCell->Close();
1278       }
1279
1280    // Obtain the fileset's read-write identifier
1281    //
1282    LPIDENT lpiRW = NULL;
1283    LPFILESET lpFileset;
1284    if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
1285       rc = FALSE;
1286    else
1287       {
1288       if ((lpiRW = lpFileset->GetReadWriteIdentifier()) == NULL)
1289          rc = FALSE;
1290       lpFileset->Close();
1291       }
1292
1293    // Perform the lock operation
1294    //
1295    if (rc)
1296       {
1297       WORKERPACKET wp;
1298       wp.wpVosVLDBEntryLock.hCell = hCell;
1299       lpiRW->GetFilesetID (&wp.wpVosVLDBEntryLock.idVolume);
1300
1301       AfsClass_Leave();
1302       rc = Worker_DoTask (wtaskVosVLDBEntryLock, &wp, &status);
1303       AfsClass_Enter();
1304       }
1305
1306    if (rc)
1307       {
1308       LPCELL lpCell;
1309       if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1310          rc = FALSE;
1311       else
1312          {
1313          if (lpiRW)
1314             lpCell->RefreshVLDB (lpiRW, TRUE, NULL, TRUE);
1315          else
1316             lpCell->RefreshVLDB (lpiFileset->GetCell());
1317          lpCell->Close();
1318          }
1319       }
1320
1321    NOTIFYCALLBACK::SendNotificationToAll (evtLockFilesetEnd, lpiFileset, status);
1322    AfsClass_Leave();
1323
1324    if (pStatus && !rc)
1325       *pStatus = status;
1326    return rc;
1327 }
1328
1329
1330 BOOL AfsClass_UnlockFileset (LPIDENT lpiFileset, ULONG *pStatus)
1331 {
1332    BOOL rc = TRUE;
1333    ULONG status;
1334
1335    AfsClass_Enter();
1336    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockFilesetBegin, lpiFileset);
1337
1338    // Obtain hCell
1339    //
1340    PVOID hCell;
1341    LPCELL lpCell;
1342    if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1343       rc = FALSE;
1344    else
1345       {
1346       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1347          rc = FALSE;
1348       lpCell->Close();
1349       }
1350
1351    // Obtain the fileset's read-write identifier
1352    //
1353    LPIDENT lpiRW = NULL;
1354    LPFILESET lpFileset;
1355    if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
1356       rc = FALSE;
1357    else
1358       {
1359       if ((lpiRW = lpFileset->GetReadWriteIdentifier()) == NULL)
1360          rc = FALSE;
1361       lpFileset->Close();
1362       }
1363
1364    // Perform the unlock operation
1365    //
1366    if (rc)
1367       {
1368       WORKERPACKET wp;
1369       wp.wpVosVLDBEntryUnlock.hCell = hCell;
1370       wp.wpVosVLDBEntryUnlock.hServer = NULL;
1371       wp.wpVosVLDBEntryUnlock.idPartition = NO_PARTITION;
1372       lpiRW->GetFilesetID (&wp.wpVosVLDBEntryUnlock.idVolume);
1373
1374       AfsClass_Leave();
1375       rc = Worker_DoTask (wtaskVosVLDBEntryUnlock, &wp, &status);
1376       AfsClass_Enter();
1377       }
1378
1379    if (rc)
1380       {
1381       LPCELL lpCell;
1382       if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1383          rc = FALSE;
1384       else
1385          {
1386          if (lpiRW)
1387             lpCell->RefreshVLDB (lpiRW, TRUE, NULL, TRUE);
1388          else
1389             lpCell->RefreshVLDB (lpiFileset->GetCell());
1390          lpCell->Close();
1391          }
1392       }
1393
1394    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockFilesetEnd, lpiFileset, status);
1395    AfsClass_Leave();
1396
1397    if (pStatus && !rc)
1398       *pStatus = status;
1399    return rc;
1400 }
1401
1402
1403 BOOL AfsClass_UnlockAllFilesets (LPIDENT lpi, ULONG *pStatus)
1404 {
1405    BOOL rc = TRUE;
1406    ULONG status;
1407
1408    AfsClass_Enter();
1409    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockAllFilesetsBegin, lpi);
1410
1411    // Obtain hCell
1412    //
1413    PVOID hCell;
1414    LPCELL lpCell;
1415    if ((lpCell = lpi->OpenCell (&status)) == NULL)
1416       rc = FALSE;
1417    else
1418       {
1419       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1420          rc = FALSE;
1421       lpCell->Close();
1422       }
1423
1424    // Obtain hServer if appropriate
1425    //
1426    PVOID hVOS = NULL;
1427    if (lpi && (!lpi->fIsCell()))
1428       {
1429       LPSERVER lpServer;
1430       if ((lpServer = lpi->OpenServer (&status)) == NULL)
1431          rc = FALSE;
1432       else
1433          {
1434          if ((hVOS = lpServer->OpenVosObject (NULL, &status)) == NULL)
1435             rc = FALSE;
1436          lpServer->Close();
1437          }
1438       }
1439
1440    // Obtain the ID of the scope aggregate.
1441    //
1442    int idPartition = NO_PARTITION;
1443    if (rc && (lpi->fIsFileset() || (lpi->fIsAggregate())))
1444       {
1445       LPAGGREGATE lpAggregate;
1446       if ((lpAggregate = lpi->OpenAggregate (&status)) == NULL)
1447          rc = FALSE;
1448       else
1449          {
1450          if ((idPartition = lpAggregate->GetID()) == NO_PARTITION)
1451             rc = FALSE;
1452          lpAggregate->Close();
1453          }
1454       }
1455
1456    // Perform the unlock operation
1457    //
1458    if (rc)
1459       {
1460       WORKERPACKET wp;
1461       wp.wpVosVLDBEntryUnlock.hCell = hCell;
1462       wp.wpVosVLDBEntryUnlock.hServer = hVOS;
1463       wp.wpVosVLDBEntryUnlock.idPartition = idPartition;
1464       wp.wpVosVLDBEntryUnlock.idVolume = NO_VOLUME;
1465
1466       AfsClass_Leave();
1467       rc = Worker_DoTask (wtaskVosVLDBEntryUnlock, &wp, &status);
1468       AfsClass_Enter();
1469       }
1470
1471    if (rc)
1472       {
1473       LPCELL lpCell;
1474       if ((lpCell = lpi->OpenCell (&status)) == NULL)
1475          rc = FALSE;
1476       else
1477          {
1478          lpCell->RefreshVLDB (lpi);
1479          lpCell->Close();
1480          }
1481       }
1482
1483    if (hVOS)
1484       {
1485       LPSERVER lpServer;
1486       if ((lpServer = lpi->OpenServer (&status)) != NULL)
1487          {
1488          lpServer->CloseVosObject();
1489          lpServer->Close();
1490          }
1491       }
1492
1493    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockAllFilesetsEnd, lpi);
1494    AfsClass_Leave();
1495
1496    if (pStatus && !rc)
1497       *pStatus = status;
1498    return rc;
1499 }
1500
1501
1502 LPIDENT AfsClass_CreateReplica (LPIDENT lpiFileset, LPIDENT lpiAggregate, ULONG *pStatus)
1503 {
1504    BOOL rc = TRUE;
1505    ULONG status;
1506    LPIDENT lpiReplica = NULL;
1507
1508    AfsClass_Enter();
1509    NOTIFYCALLBACK::SendNotificationToAll (evtCreateReplicaBegin, lpiFileset, lpiAggregate, NULL, NULL, 0, 0);
1510
1511    // Obtain hCell and hVOS for the target server
1512    //
1513    PVOID hCell;
1514    PVOID hVOS = NULL;
1515    LPSERVER lpServer;
1516    if ((lpServer = lpiAggregate->OpenServer (&status)) == NULL)
1517       rc = FALSE;
1518    else
1519       {
1520       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
1521          rc = FALSE;
1522       lpServer->Close();
1523       }
1524
1525    // Obtain idPartition
1526    //
1527    int idPartition;
1528    LPAGGREGATE lpAggregate;
1529    if ((lpAggregate = lpiAggregate->OpenAggregate (&status)) == NULL)
1530       rc = FALSE;
1531    else
1532       {
1533       idPartition = lpAggregate->GetID();
1534       lpAggregate->Close();
1535       }
1536
1537    // Modify VLDB to create mention of a new replica
1538    //
1539    if (rc)
1540       {
1541       WORKERPACKET wp;
1542       wp.wpVosVLDBReadOnlySiteCreate.hCell = hCell;
1543       wp.wpVosVLDBReadOnlySiteCreate.hServer = hVOS;
1544       wp.wpVosVLDBReadOnlySiteCreate.idPartition = idPartition;
1545       lpiFileset->GetFilesetID (&wp.wpVosVLDBReadOnlySiteCreate.idVolume);
1546
1547       AfsClass_Leave();
1548       rc = Worker_DoTask (wtaskVosVLDBReadOnlySiteCreate, &wp, &status);
1549       AfsClass_Enter();
1550       }
1551
1552    // Clean up
1553    //
1554    if (rc)
1555       {
1556       LPAGGREGATE lpAggregate;
1557       if ((lpAggregate = lpiAggregate->OpenAggregate (&status)) == NULL)
1558          rc = FALSE;
1559       else
1560          {
1561          lpAggregate->Invalidate();
1562          lpAggregate->RefreshFilesets (TRUE, &status);
1563          lpAggregate->Close();
1564          }
1565       }
1566
1567    if (rc)
1568       {
1569       LPCELL lpCell;
1570       if ((lpCell = lpiAggregate->OpenCell()) == NULL)
1571          rc = FALSE;
1572       else
1573          {
1574          lpCell->RefreshVLDB (lpiAggregate);
1575          lpCell->Close();
1576          }
1577       }
1578
1579    if (rc)
1580       {
1581       LPFILESET lpFileset;
1582       if ((lpFileset = lpiFileset->OpenFileset (&status)) == NULL)
1583          rc = FALSE;
1584       else
1585          {
1586          if ((lpiReplica = lpFileset->GetReadOnlyIdentifier (lpiAggregate, &status)) == NULL)
1587             rc = FALSE;
1588          lpFileset->Close();
1589          }
1590       }
1591
1592    if (hVOS)
1593       {
1594       if ((lpServer = lpiAggregate->OpenServer (&status)) != NULL)
1595          {
1596          lpServer->CloseVosObject();
1597          lpServer->Close();
1598          }
1599       }
1600
1601    NOTIFYCALLBACK::SendNotificationToAll (evtCreateReplicaEnd, lpiFileset, lpiAggregate, NULL, NULL, 0, status);
1602    AfsClass_Leave();
1603
1604    if (pStatus && !rc)
1605       *pStatus = status;
1606    return (rc) ? lpiReplica : FALSE;
1607 }
1608
1609
1610 BOOL AfsClass_DeleteReplica (LPIDENT lpiReplica, ULONG *pStatus)
1611 {
1612    BOOL rc = TRUE;
1613    ULONG status;
1614
1615    AfsClass_Enter();
1616    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteFilesetBegin, lpiReplica);
1617
1618    // Obtain hCell and hVOS for the server
1619    //
1620    PVOID hCell;
1621    PVOID hVOS = NULL;
1622    LPSERVER lpServer;
1623    if ((lpServer = lpiReplica->OpenServer (&status)) == NULL)
1624       rc = FALSE;
1625    else
1626       {
1627       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
1628          rc = FALSE;
1629       lpServer->Close();
1630       }
1631
1632    // Get the read/write fileset identifier and Ghost status
1633    //
1634    LPIDENT lpiRW = NULL;
1635    int wFilesetGhost = 0;
1636    LPFILESET lpFileset;
1637    if ((lpFileset = lpiReplica->OpenFileset (&status)) == NULL)
1638       rc = FALSE;
1639    else
1640       {
1641       wFilesetGhost = lpFileset->GetGhostStatus();
1642       if ((lpiRW = lpFileset->GetReadWriteIdentifier()) == NULL)
1643          rc = FALSE;
1644       lpFileset->Close();
1645       }
1646
1647    TCHAR szAggregateName[ cchNAME ];
1648    lpiReplica->GetAggregateName (szAggregateName);
1649
1650    // Obtain the ID of the replica's partition
1651    //
1652    int idPartition;
1653    LPAGGREGATE lpAggregate;
1654    if ((lpAggregate = lpiReplica->OpenAggregate (&status)) == NULL)
1655       rc = FALSE;
1656    else
1657       {
1658       idPartition = lpAggregate->GetID();
1659       lpAggregate->Close();
1660       }
1661
1662    // If the volume exists in both VLDB and on the server, just delete it
1663    //
1664    if (rc && (wFilesetGhost & GHOST_HAS_VLDB_ENTRY) && (wFilesetGhost & GHOST_HAS_SERVER_ENTRY))
1665       {
1666       WORKERPACKET wp;
1667       wp.wpVosVolumeDelete.hCell = hCell;
1668       wp.wpVosVolumeDelete.hServer = hVOS;
1669       wp.wpVosVolumeDelete.idPartition = idPartition;
1670       lpiReplica->GetFilesetID (&wp.wpVosVolumeDelete.idVolume);
1671
1672       AfsClass_Leave();
1673       rc = Worker_DoTask (wtaskVosVolumeDelete, &wp, &status);
1674       AfsClass_Enter();
1675       }
1676    else
1677       {
1678       // If necessary, modify VLDB to remove mention of this replica
1679       //
1680       if (rc && (wFilesetGhost & GHOST_HAS_VLDB_ENTRY))
1681          {
1682          WORKERPACKET wp;
1683          wp.wpVosVLDBReadOnlySiteDelete.hCell = hCell;
1684          wp.wpVosVLDBReadOnlySiteDelete.hServer = hVOS;
1685          wp.wpVosVLDBReadOnlySiteDelete.idPartition = idPartition;
1686          lpiRW->GetFilesetID (&wp.wpVosVLDBReadOnlySiteDelete.idVolume);
1687
1688          AfsClass_Leave();
1689          rc = Worker_DoTask (wtaskVosVLDBReadOnlySiteDelete, &wp, &status);
1690          AfsClass_Enter();
1691          }
1692
1693       // If necessary, zap the volume
1694       //
1695       if (rc && (wFilesetGhost & GHOST_HAS_SERVER_ENTRY))
1696          {
1697          WORKERPACKET wp;
1698          wp.wpVosVolumeZap.hCell = hCell;
1699          wp.wpVosVolumeZap.hServer = hVOS;
1700          wp.wpVosVolumeZap.idPartition = idPartition;
1701          lpiReplica->GetFilesetID (&wp.wpVosVolumeZap.idVolume);
1702          wp.wpVosVolumeZap.fForce = TRUE;
1703
1704          AfsClass_Leave();
1705          rc = Worker_DoTask (wtaskVosVolumeZap, &wp, &status);
1706          AfsClass_Enter();
1707          }
1708       }
1709
1710    // Clean up
1711    //
1712    if (rc)
1713       {
1714       LPAGGREGATE lpAggregate;
1715       if ((lpAggregate = lpiReplica->OpenAggregate (&status)) == NULL)
1716          rc = FALSE;
1717       else
1718          {
1719          lpAggregate->Invalidate();
1720          lpAggregate->RefreshFilesets (TRUE, &status);
1721          lpAggregate->Close();
1722          }
1723       }
1724
1725    if (rc)
1726       {
1727       LPCELL lpCell;
1728       if ((lpCell = lpiReplica->OpenCell()) == NULL)
1729          rc = FALSE;
1730       else
1731          {
1732          lpCell->RefreshVLDB (lpiReplica->GetAggregate());
1733          lpCell->Close();
1734          }
1735       }
1736
1737    if (hVOS)
1738       {
1739       if ((lpServer = lpiReplica->OpenServer (&status)) != NULL)
1740          {
1741          lpServer->CloseVosObject();
1742          lpServer->Close();
1743          }
1744       }
1745
1746    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteFilesetEnd, lpiReplica, status);
1747    AfsClass_Leave();
1748
1749    if (pStatus && !rc)
1750       *pStatus = status;
1751    return rc;
1752 }
1753
1754
1755 BOOL AfsClass_DeleteClone (LPIDENT lpiClone, ULONG *pStatus)
1756 {
1757    return AfsClass_DeleteFileset (lpiClone, TRUE, TRUE, pStatus);
1758 }
1759
1760
1761 BOOL AfsClass_InstallFile (LPIDENT lpiServer, LPTSTR pszTarget, LPTSTR pszSource, ULONG *pStatus)
1762 {
1763    BOOL rc = TRUE;
1764    ULONG status;
1765
1766    AfsClass_Enter();
1767    NOTIFYCALLBACK::SendNotificationToAll (evtInstallFileBegin, lpiServer, pszSource, 0);
1768
1769    PVOID hCell;
1770    PVOID hBOS;
1771    LPSERVER lpServer;
1772    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
1773       rc = FALSE;
1774    else
1775       {
1776       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
1777          rc = FALSE;
1778       lpServer->Close();
1779       }
1780
1781    if (rc)
1782       {
1783       WORKERPACKET wp;
1784       wp.wpBosExecutableCreate.hServer = hBOS;
1785       wp.wpBosExecutableCreate.pszLocal = pszSource;
1786       wp.wpBosExecutableCreate.pszRemoteDir = pszTarget;
1787
1788       AfsClass_Leave();
1789       rc = Worker_DoTask (wtaskBosExecutableCreate, &wp, &status);
1790       AfsClass_Enter();
1791       }
1792
1793    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
1794       {
1795       lpServer->CloseBosObject();
1796       lpServer->Close();
1797       }
1798
1799    NOTIFYCALLBACK::SendNotificationToAll (evtInstallFileEnd, lpiServer, pszSource, status);
1800    AfsClass_Leave();
1801
1802    if (pStatus && !rc)
1803       *pStatus = status;
1804    return rc;
1805 }
1806
1807
1808 BOOL AfsClass_UninstallFile (LPIDENT lpiServer, LPTSTR pszUninstall, ULONG *pStatus)
1809 {
1810    BOOL rc = TRUE;
1811    ULONG status;
1812
1813    AfsClass_Enter();
1814    NOTIFYCALLBACK::SendNotificationToAll (evtUninstallFileBegin, lpiServer, pszUninstall, 0);
1815
1816    PVOID hCell;
1817    PVOID hBOS;
1818    LPSERVER lpServer;
1819    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
1820       rc = FALSE;
1821    else
1822       {
1823       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
1824          rc = FALSE;
1825       lpServer->Close();
1826       }
1827
1828    if (rc)
1829       {
1830       WORKERPACKET wp;
1831       wp.wpBosExecutableRevert.hServer = hBOS;
1832       wp.wpBosExecutableRevert.pszFilename = pszUninstall;
1833
1834       AfsClass_Leave();
1835       rc = Worker_DoTask (wtaskBosExecutableRevert, &wp, &status);
1836       AfsClass_Enter();
1837       }
1838
1839    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
1840       {
1841       lpServer->CloseBosObject();
1842       lpServer->Close();
1843       }
1844
1845    NOTIFYCALLBACK::SendNotificationToAll (evtUninstallFileEnd, lpiServer, pszUninstall, status);
1846    AfsClass_Leave();
1847
1848    if (pStatus && !rc)
1849       *pStatus = status;
1850    return rc;
1851 }
1852
1853
1854 BOOL AfsClass_PruneOldFiles (LPIDENT lpiServer, BOOL fBAK, BOOL fOLD, BOOL fCore, ULONG *pStatus)
1855 {
1856    BOOL rc = TRUE;
1857    ULONG status;
1858
1859    AfsClass_Enter();
1860    NOTIFYCALLBACK::SendNotificationToAll (evtPruneFilesBegin, lpiServer);
1861
1862    PVOID hCell;
1863    PVOID hBOS;
1864    LPSERVER lpServer;
1865    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
1866       rc = FALSE;
1867    else
1868       {
1869       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
1870          rc = FALSE;
1871       lpServer->Close();
1872       }
1873
1874    if (rc)
1875       {
1876       WORKERPACKET wp;
1877       wp.wpBosExecutablePrune.hServer = hBOS;
1878       wp.wpBosExecutablePrune.fPruneBak = fBAK;
1879       wp.wpBosExecutablePrune.fPruneOld = fOLD;
1880       wp.wpBosExecutablePrune.fPruneCore = fCore;
1881
1882       AfsClass_Leave();
1883       rc = Worker_DoTask (wtaskBosExecutablePrune, &wp, &status);
1884       AfsClass_Enter();
1885       }
1886
1887    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
1888       {
1889       lpServer->CloseBosObject();
1890       lpServer->Close();
1891       }
1892
1893    NOTIFYCALLBACK::SendNotificationToAll (evtPruneFilesEnd, lpiServer, status);
1894    AfsClass_Leave();
1895
1896    if (pStatus && !rc)
1897       *pStatus = status;
1898    return rc;
1899 }
1900
1901
1902 BOOL AfsClass_RenameFileset (LPIDENT lpiFileset, LPTSTR pszNewName, ULONG *pStatus)
1903 {
1904    BOOL rc = TRUE;
1905    ULONG status;
1906
1907    AfsClass_Enter();
1908    NOTIFYCALLBACK::SendNotificationToAll (evtRenameFilesetBegin, lpiFileset, pszNewName, 0);
1909
1910    PVOID hCell;
1911    LPCELL lpCell;
1912    if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1913       rc = FALSE;
1914    else
1915       {
1916       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
1917          rc = FALSE;
1918       lpCell->Close();
1919       }
1920
1921    if (rc)
1922       {
1923       WORKERPACKET wp;
1924       wp.wpVosVolumeRename.hCell = hCell;
1925       lpiFileset->GetFilesetID (&wp.wpVosVolumeRename.idVolume);
1926       wp.wpVosVolumeRename.pszVolume = pszNewName;
1927
1928       AfsClass_Leave();
1929       rc = Worker_DoTask (wtaskVosVolumeRename, &wp, &status);
1930       AfsClass_Enter();
1931       }
1932
1933    if (rc)
1934       {
1935       LPCELL lpCell;
1936       if ((lpCell = lpiFileset->OpenCell (&status)) == NULL)
1937          rc = FALSE;
1938       else
1939          {
1940          lpCell->Invalidate();
1941          rc = lpCell->RefreshAll (&status);
1942          lpCell->Close();
1943          }
1944       }
1945
1946
1947    NOTIFYCALLBACK::SendNotificationToAll (evtRenameFilesetEnd, lpiFileset, pszNewName, status);
1948    AfsClass_Leave();
1949
1950    if (pStatus && !rc)
1951       *pStatus = status;
1952    return rc;
1953 }
1954
1955
1956 #define iswhite(_ch) ((_ch)==TEXT(' ') || (_ch)==TEXT('\t'))
1957
1958 LPIDENT AfsClass_CreateService (LPIDENT lpiServer, LPTSTR pszService, LPTSTR pszCommand, LPTSTR pszParams, LPTSTR pszNotifier, AFSSERVICETYPE type, SYSTEMTIME *pstIfCron, ULONG *pStatus)
1959 {
1960    BOOL rc = TRUE;
1961    ULONG status;
1962    LPIDENT lpiService = NULL;
1963
1964    AfsClass_Enter();
1965    NOTIFYCALLBACK::SendNotificationToAll (evtCreateServiceBegin, lpiServer, pszService, 0);
1966
1967    PVOID hCell;
1968    PVOID hBOS;
1969    LPSERVER lpServer;
1970    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
1971       rc = FALSE;
1972    else
1973       {
1974       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
1975          rc = FALSE;
1976       lpServer->Close();
1977       }
1978
1979    if (rc)
1980       {
1981       WORKERPACKET wp;
1982       wp.wpBosProcessCreate.hServer = hBOS;
1983       wp.wpBosProcessCreate.pszService = pszService;
1984       wp.wpBosProcessCreate.type = type;
1985       wp.wpBosProcessCreate.pszNotifier = pszNotifier;
1986
1987       TCHAR szFullCommand[ MAX_PATH + MAX_PATH ];
1988       wsprintf (szFullCommand, TEXT("%s %s"), pszCommand, pszParams);
1989       wp.wpBosProcessCreate.pszCommand = szFullCommand;
1990
1991       TCHAR szCronTime[ 256 ] = TEXT("");
1992       wp.wpBosProcessCreate.pszTimeCron = szCronTime;
1993
1994       if (type == SERVICETYPE_CRON)
1995          AfsClass_FormatRecurringTime (szCronTime, pstIfCron);
1996       else
1997          wp.wpBosProcessCreate.pszTimeCron = NULL;
1998
1999       AfsClass_Leave();
2000       rc = Worker_DoTask (wtaskBosProcessCreate, &wp, &status);
2001       AfsClass_Enter();
2002       }
2003
2004    if (rc)
2005       {
2006       if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2007          rc = FALSE;
2008       else
2009          {
2010          lpServer->InvalidateServices();
2011          if (!lpServer->RefreshServices (TRUE, &status))
2012             rc = FALSE;
2013          else
2014             {
2015             LPSERVICE lpService;
2016             if ((lpService = lpServer->OpenService (pszService, &status)) == NULL)
2017                rc = FALSE;
2018             else
2019                {
2020                lpiService = lpService->GetIdentifier();
2021                lpService->Close();
2022                }
2023             }
2024          lpServer->Close();
2025          }
2026       }
2027
2028    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2029       {
2030       lpServer->CloseBosObject();
2031       lpServer->Close();
2032       }
2033
2034    NOTIFYCALLBACK::SendNotificationToAll (evtCreateServiceEnd, lpiServer, pszService, status);
2035    AfsClass_Leave();
2036
2037    if (pStatus && !rc)
2038       *pStatus = status;
2039    return (rc) ? lpiService : NULL;
2040 }
2041
2042
2043 BOOL AfsClass_DeleteService (LPIDENT lpiService, ULONG *pStatus)
2044 {
2045    BOOL rc = TRUE;
2046    ULONG status;
2047
2048    AfsClass_Enter();
2049    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteServiceBegin, lpiService);
2050
2051    PVOID hCell;
2052    PVOID hBOS;
2053    LPSERVER lpServer;
2054    if ((lpServer = lpiService->OpenServer (&status)) == NULL)
2055       rc = FALSE;
2056    else
2057       {
2058       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2059          rc = FALSE;
2060       lpServer->Close();
2061       }
2062
2063    // Before a service can be deleted, it must be stopped (otherwise, on NT,
2064    // the Delete operation won't block for the required Stop to complete--
2065    // so our wtaskDeleteBosProcess would return before the service really
2066    // was deleted).
2067    //
2068    if (rc)
2069       {
2070       TCHAR szService[ cchNAME ];
2071       lpiService->GetServiceName (szService);
2072
2073       WORKERPACKET wp;
2074       wp.wpBosProcessExecutionStateSet.hServer = hBOS;
2075       wp.wpBosProcessExecutionStateSet.pszService = szService;
2076       wp.wpBosProcessExecutionStateSet.state = SERVICESTATE_STOPPED;
2077       // TODO: wp.wpStopBosProcess.fWait = TRUE;
2078
2079       AfsClass_Leave();
2080       rc = Worker_DoTask (wtaskBosProcessExecutionStateSet, &wp, &status);
2081       AfsClass_Enter();
2082       }
2083
2084    // Delete the service
2085    //
2086    if (rc)
2087       {
2088       TCHAR szService[ cchNAME ];
2089       lpiService->GetServiceName (szService);
2090
2091       WORKERPACKET wp;
2092       wp.wpBosProcessDelete.hServer = hBOS;
2093       wp.wpBosProcessDelete.pszService = szService;
2094
2095       AfsClass_Leave();
2096       rc = Worker_DoTask (wtaskBosProcessDelete, &wp, &status);
2097       AfsClass_Enter();
2098       }
2099
2100    if (rc)
2101       {
2102       if ((lpServer = lpiService->OpenServer (&status)) == NULL)
2103          rc = FALSE;
2104       else
2105          {
2106          lpServer->InvalidateServices();
2107          if (!lpServer->RefreshServices (TRUE, &status))
2108             rc = FALSE;
2109          lpServer->Close();
2110          }
2111       }
2112
2113    if ((lpServer = lpiService->OpenServer (&status)) != NULL)
2114       {
2115       lpServer->CloseBosObject();
2116       lpServer->Close();
2117       }
2118
2119    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteServiceEnd, lpiService, status);
2120    AfsClass_Leave();
2121
2122    if (pStatus && !rc)
2123       *pStatus = status;
2124    return rc;
2125 }
2126
2127
2128 BOOL AfsClass_ReleaseFileset (LPIDENT lpiFilesetRW, BOOL fForce, ULONG *pStatus)
2129 {
2130    BOOL rc = TRUE;
2131    ULONG status;
2132
2133    AfsClass_Enter();
2134    NOTIFYCALLBACK::SendNotificationToAll (evtReleaseFilesetBegin, lpiFilesetRW);
2135
2136    // Obtain hCell and hVOS
2137    //
2138    PVOID hCell;
2139    PVOID hVOS = NULL;
2140    LPSERVER lpServer;
2141    if ((lpServer = lpiFilesetRW->OpenServer (&status)) == NULL)
2142       rc = FALSE;
2143    else
2144       {
2145       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
2146          rc = FALSE;
2147       lpServer->Close();
2148       }
2149
2150    // Perform the actual operation
2151    //
2152    if (rc)
2153       {
2154       WORKERPACKET wp;
2155       wp.wpVosVolumeRelease.hCell = hCell;
2156       wp.wpVosVolumeRelease.fForce = fForce;
2157       lpiFilesetRW->GetFilesetID (&wp.wpVosVolumeRelease.idVolume);
2158
2159       AfsClass_Leave();
2160       rc = Worker_DoTask (wtaskVosVolumeRelease, &wp, &status);
2161       AfsClass_Enter();
2162       }
2163
2164    // Clean up
2165    //
2166    if (rc)
2167       {
2168       LPCELL lpCell;
2169       if ((lpCell = lpiFilesetRW->OpenCell (&status)) == NULL)
2170          rc = FALSE;
2171       else
2172          {
2173          lpCell->Invalidate();
2174          rc = lpCell->RefreshAll (&status);
2175          lpCell->Close();
2176          }
2177       }
2178
2179    if (hVOS)
2180       {
2181       if ((lpServer = lpiFilesetRW->OpenServer (&status)) != NULL)
2182          {
2183          lpServer->CloseVosObject();
2184          lpServer->Close();
2185          }
2186       }
2187
2188    NOTIFYCALLBACK::SendNotificationToAll (evtReleaseFilesetEnd, lpiFilesetRW, status);
2189    AfsClass_Leave();
2190
2191    if (pStatus && !rc)
2192       *pStatus = status;
2193    return rc;
2194 }
2195
2196
2197 BOOL AfsClass_GetFileDates (LPIDENT lpiServer, LPTSTR pszFilename, SYSTEMTIME *pstFile, SYSTEMTIME *pstBAK, SYSTEMTIME *pstOLD, ULONG *pStatus)
2198 {
2199    BOOL rc = TRUE;
2200    ULONG status;
2201
2202    AfsClass_Enter();
2203    NOTIFYCALLBACK::SendNotificationToAll (evtGetFileDatesBegin, lpiServer, pszFilename, 0);
2204
2205    PVOID hCell;
2206    PVOID hBOS;
2207    LPSERVER lpServer;
2208    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2209       rc = FALSE;
2210    else
2211       {
2212       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2213          rc = FALSE;
2214       lpServer->Close();
2215       }
2216
2217    if (rc)
2218       {
2219       WORKERPACKET wp;
2220       wp.wpBosExecutableTimestampGet.hServer = hBOS;
2221       wp.wpBosExecutableTimestampGet.pszFilename = pszFilename;
2222
2223       AfsClass_Leave();
2224       if ((rc = Worker_DoTask (wtaskBosExecutableTimestampGet, &wp, &status)) == TRUE)
2225          {
2226          *pstFile = wp.wpBosExecutableTimestampGet.timeNew;
2227          *pstBAK = wp.wpBosExecutableTimestampGet.timeBak;
2228          *pstOLD = wp.wpBosExecutableTimestampGet.timeOld;
2229          }
2230
2231       AfsClass_Enter();
2232       }
2233
2234    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2235       {
2236       lpServer->CloseBosObject();
2237       lpServer->Close();
2238       }
2239
2240    NOTIFYCALLBACK::SendNotificationToAll (evtGetFileDatesEnd, lpiServer, pszFilename, status);
2241    AfsClass_Leave();
2242
2243    if (pStatus && !rc)
2244       *pStatus = status;
2245    return rc;
2246 }
2247
2248
2249 BOOL AfsClass_ExecuteCommand (LPIDENT lpiServer, LPTSTR pszCommand, ULONG *pStatus)
2250 {
2251    BOOL rc = TRUE;
2252    ULONG status;
2253
2254    AfsClass_Enter();
2255    NOTIFYCALLBACK::SendNotificationToAll (evtExecuteCommandBegin, lpiServer, pszCommand, 0);
2256
2257    PVOID hCell;
2258    PVOID hBOS;
2259    LPSERVER lpServer;
2260    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2261       rc = FALSE;
2262    else
2263       {
2264       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2265          rc = FALSE;
2266       lpServer->Close();
2267       }
2268
2269    if (rc)
2270       {
2271       WORKERPACKET wp;
2272       wp.wpBosCommandExecute.hServer = hBOS;
2273       wp.wpBosCommandExecute.pszCommand = pszCommand;
2274
2275       AfsClass_Leave();
2276       rc = Worker_DoTask (wtaskBosCommandExecute, &wp, &status);
2277       AfsClass_Enter();
2278       }
2279
2280    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2281       {
2282       lpServer->CloseBosObject();
2283       lpServer->Close();
2284       }
2285
2286    NOTIFYCALLBACK::SendNotificationToAll (evtExecuteCommandEnd, lpiServer, pszCommand, status);
2287    AfsClass_Leave();
2288
2289    if (pStatus && !rc)
2290       *pStatus = status;
2291    return rc;
2292 }
2293
2294
2295 LPADMINLIST AfsClass_AdminList_Load (LPIDENT lpiServer, ULONG *pStatus)
2296 {
2297    BOOL rc = TRUE;
2298    ULONG status;
2299    LPADMINLIST lpList = NULL;
2300
2301    AfsClass_Enter();
2302    NOTIFYCALLBACK::SendNotificationToAll (evtAdminListLoadBegin, lpiServer);
2303
2304    PVOID hCell;
2305    PVOID hBOS;
2306    LPSERVER lpServer;
2307    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2308       rc = FALSE;
2309    else
2310       {
2311       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2312          rc = FALSE;
2313       lpServer->Close();
2314       }
2315
2316    if (rc)
2317       {
2318       lpList = New(ADMINLIST);
2319       memset (lpList, 0x00, sizeof(ADMINLIST));
2320       lpList->cRef = 1;
2321       lpList->lpiServer = lpiServer;
2322
2323       WORKERPACKET wpBegin;
2324       wpBegin.wpBosAdminGetBegin.hServer = hBOS;
2325       if (!Worker_DoTask (wtaskBosAdminGetBegin, &wpBegin, &status))
2326          rc = FALSE;
2327       else
2328          {
2329          for (;;)
2330             {
2331             TCHAR szAdmin[ cchNAME ];
2332
2333             WORKERPACKET wpNext;
2334             wpNext.wpBosAdminGetNext.hEnum = wpBegin.wpBosAdminGetBegin.hEnum;
2335             wpNext.wpBosAdminGetNext.pszAdmin = szAdmin;
2336
2337             if (!Worker_DoTask (wtaskBosAdminGetNext, &wpNext, &status))
2338                {
2339                if (status == ADMITERATORDONE)
2340                   status = 0;
2341                else
2342                   rc = FALSE;
2343                break;
2344                }
2345
2346             size_t iAdded;
2347             if ((iAdded = AfsClass_AdminList_AddEntry (lpList, szAdmin)) != (size_t)-1)
2348                {
2349                lpList->aEntries[ iAdded ].fAdded = FALSE;
2350                }
2351             }
2352
2353          WORKERPACKET wpDone;
2354          wpDone.wpBosAdminGetDone.hEnum = wpBegin.wpBosAdminGetBegin.hEnum;
2355          Worker_DoTask (wtaskBosAdminGetDone, &wpDone);
2356          }
2357       }
2358
2359    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2360       {
2361       lpServer->CloseBosObject();
2362       lpServer->Close();
2363       }
2364
2365    NOTIFYCALLBACK::SendNotificationToAll (evtAdminListLoadEnd, lpiServer, status);
2366    AfsClass_Leave();
2367
2368    if (pStatus && !rc)
2369       *pStatus = status;
2370    return (rc) ? lpList : NULL;
2371 }
2372
2373
2374 LPADMINLIST AfsClass_AdminList_Copy (LPADMINLIST lpOld)
2375 {
2376    LPADMINLIST lpNew = NULL;
2377
2378    if (lpOld)
2379       {
2380       lpNew = New(ADMINLIST);
2381       memcpy (lpNew, lpOld, sizeof(ADMINLIST));
2382
2383       lpNew->cRef = 1;
2384       lpNew->aEntries = 0;
2385       lpNew->cEntries = 0;
2386
2387       if (REALLOC (lpNew->aEntries, lpNew->cEntries, lpOld->cEntries, cREALLOC_ADMINLISTENTRIES))
2388          {
2389          size_t cb = lpOld->cEntries * sizeof(ADMINLISTENTRY);
2390          memcpy (lpNew->aEntries, lpOld->aEntries, cb);
2391          }
2392       }
2393
2394    return lpNew;
2395 }
2396
2397
2398 BOOL AfsClass_AdminList_Save (LPADMINLIST lpList, ULONG *pStatus)
2399 {
2400    BOOL rc = TRUE;
2401    ULONG status;
2402
2403    AfsClass_Enter();
2404    NOTIFYCALLBACK::SendNotificationToAll (evtAdminListSaveBegin, lpList->lpiServer);
2405
2406    PVOID hCell;
2407    PVOID hBOS;
2408    LPSERVER lpServer;
2409    if ((lpServer = lpList->lpiServer->OpenServer (&status)) == NULL)
2410       rc = FALSE;
2411    else
2412       {
2413       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2414          rc = FALSE;
2415       lpServer->Close();
2416       }
2417
2418    if (rc)
2419       {
2420       for (size_t iEntry = 0; iEntry < lpList->cEntries; ++iEntry)
2421          {
2422          if (!lpList->aEntries[ iEntry ].szAdmin[0])
2423             continue;
2424
2425          // are we supposed to add this entry?
2426          //
2427          if (lpList->aEntries[ iEntry ].fAdded && !lpList->aEntries[ iEntry ].fDeleted)
2428             {
2429             WORKERPACKET wp;
2430             wp.wpBosAdminCreate.hServer = hBOS;
2431             wp.wpBosAdminCreate.pszAdmin = lpList->aEntries[ iEntry ].szAdmin;
2432
2433             ULONG thisstatus;
2434             if (!Worker_DoTask (wtaskBosAdminCreate, &wp, &thisstatus))
2435                {
2436                rc = FALSE;
2437                status = thisstatus;
2438                }
2439             else
2440                {
2441                lpList->aEntries[ iEntry ].fAdded = FALSE;
2442                }
2443             }
2444
2445          // are we supposed to delete this entry?
2446          //
2447          if (!lpList->aEntries[ iEntry ].fAdded && lpList->aEntries[ iEntry ].fDeleted)
2448             {
2449             WORKERPACKET wp;
2450             wp.wpBosAdminDelete.hServer = hBOS;
2451             wp.wpBosAdminDelete.pszAdmin = lpList->aEntries[ iEntry ].szAdmin;
2452
2453             ULONG thisstatus;
2454             if (!Worker_DoTask (wtaskBosAdminDelete, &wp, &thisstatus))
2455                {
2456                rc = FALSE;
2457                status = thisstatus;
2458                }
2459             else
2460                {
2461                lpList->aEntries[ iEntry ].szAdmin[0] = TEXT('\0');
2462                lpList->aEntries[ iEntry ].fDeleted = FALSE;
2463                }
2464             }
2465          }
2466       }
2467
2468    if ((lpServer = lpList->lpiServer->OpenServer (&status)) != NULL)
2469       {
2470       lpServer->CloseBosObject();
2471       lpServer->Close();
2472       }
2473
2474    NOTIFYCALLBACK::SendNotificationToAll (evtAdminListSaveEnd, lpList->lpiServer, status);
2475    AfsClass_Leave();
2476
2477    if (pStatus && !rc)
2478       *pStatus = status;
2479    return rc;
2480 }
2481
2482
2483 void AfsClass_AdminList_Free (LPADMINLIST lpList)
2484 {
2485    if (lpList && !InterlockedDecrement (&lpList->cRef))
2486       {
2487       if (lpList->aEntries)
2488          Free (lpList->aEntries);
2489       memset (lpList, 0x00, sizeof(ADMINLIST));
2490       Delete (lpList);
2491       }
2492 }
2493
2494
2495 size_t AfsClass_AdminList_AddEntry (LPADMINLIST lpList, LPTSTR pszAdmin)
2496 {
2497    size_t iAdded = (size_t)-1;
2498
2499    if (lpList)
2500       {
2501       size_t iEntry;
2502       for (iEntry = 0; iEntry < lpList->cEntries; ++iEntry)
2503          {
2504          if (!lpList->aEntries[ iEntry ].szAdmin[0])
2505             break;
2506          }
2507       if (iEntry >= lpList->cEntries)
2508          {
2509          (void)REALLOC (lpList->aEntries, lpList->cEntries, 1+iEntry, cREALLOC_ADMINLISTENTRIES);
2510          }
2511       if (iEntry < lpList->cEntries)
2512          {
2513          iAdded = iEntry;
2514          lstrcpy (lpList->aEntries[ iAdded ].szAdmin, pszAdmin);
2515          lpList->aEntries[ iAdded ].fAdded = TRUE;
2516          lpList->aEntries[ iAdded ].fDeleted = FALSE;
2517          }
2518       }
2519
2520    return iAdded;
2521 }
2522
2523
2524 BOOL AfsClass_AdminList_DelEntry (LPADMINLIST lpList, size_t iIndex)
2525 {
2526    BOOL rc = FALSE;
2527
2528    if ( lpList &&
2529         (iIndex < lpList->cEntries) &&
2530         (lpList->aEntries[ iIndex ].szAdmin[0]) &&
2531         (!lpList->aEntries[ iIndex ].fDeleted) )
2532       {
2533       if (lpList->aEntries[ iIndex ].fAdded)
2534          lpList->aEntries[ iIndex ].szAdmin[0] = TEXT('\0');
2535       else
2536          lpList->aEntries[ iIndex ].fDeleted = TRUE;
2537
2538       rc = TRUE;
2539       }
2540
2541    return rc;
2542 }
2543
2544
2545 LPKEYLIST AfsClass_KeyList_Load (LPIDENT lpiServer, ULONG *pStatus)
2546 {
2547    BOOL rc = TRUE;
2548    ULONG status;
2549    LPKEYLIST lpList = NULL;
2550
2551    AfsClass_Enter();
2552    NOTIFYCALLBACK::SendNotificationToAll (evtKeyListLoadBegin, lpiServer);
2553
2554    PVOID hCell;
2555    PVOID hBOS;
2556    LPSERVER lpServer;
2557    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2558       rc = FALSE;
2559    else
2560       {
2561       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2562          rc = FALSE;
2563       lpServer->Close();
2564       }
2565
2566    if (rc)
2567       {
2568       lpList = New(KEYLIST);
2569       memset (lpList, 0x00, sizeof(KEYLIST));
2570       lpList->lpiServer = lpiServer;
2571
2572       WORKERPACKET wpBegin;
2573       wpBegin.wpBosKeyGetBegin.hServer = hBOS;
2574       if (!Worker_DoTask (wtaskBosKeyGetBegin, &wpBegin, &status))
2575          rc = FALSE;
2576       else
2577          {
2578          for (size_t iEnum = 0; ; ++iEnum)
2579             {
2580             WORKERPACKET wpNext;
2581             wpNext.wpBosKeyGetNext.hEnum = wpBegin.wpBosKeyGetBegin.hEnum;
2582
2583             if (!Worker_DoTask (wtaskBosKeyGetNext, &wpNext, &status))
2584                {
2585                if (status == ADMITERATORDONE)
2586                   status = 0;
2587                else
2588                   rc = FALSE;
2589                break;
2590                }
2591
2592             if (REALLOC (lpList->aKeys, lpList->cKeys, 1+iEnum, cREALLOC_SERVERKEYS))
2593                {
2594                lpList->aKeys[ iEnum ].keyVersion = wpNext.wpBosKeyGetNext.keyVersion;
2595                memcpy (&lpList->aKeys[ iEnum ].keyData, &wpNext.wpBosKeyGetNext.keyData, sizeof(ENCRYPTIONKEY));
2596                memcpy (&lpList->aKeys[ iEnum ].keyInfo, &wpNext.wpBosKeyGetNext.keyInfo, sizeof(ENCRYPTIONKEYINFO));
2597                }
2598             }
2599
2600          WORKERPACKET wpDone;
2601          wpDone.wpBosKeyGetDone.hEnum = wpBegin.wpBosKeyGetBegin.hEnum;
2602          Worker_DoTask (wtaskBosKeyGetDone, &wpDone);
2603          }
2604       }
2605
2606    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2607       {
2608       lpServer->CloseBosObject();
2609       lpServer->Close();
2610       }
2611
2612    NOTIFYCALLBACK::SendNotificationToAll (evtKeyListLoadEnd, lpiServer, status);
2613    AfsClass_Leave();
2614
2615    if (pStatus && !rc)
2616       *pStatus = status;
2617    return (rc) ? lpList : NULL;
2618 }
2619
2620
2621 void AfsClass_KeyList_Free (LPKEYLIST lpList)
2622 {
2623    if (lpList)
2624       {
2625       if (lpList->aKeys)
2626          Free (lpList->aKeys);
2627       memset (lpList, 0x00, sizeof(KEYLIST));
2628       Delete (lpList);
2629       }
2630 }
2631
2632
2633 BOOL AfsClass_AddKey (LPIDENT lpiServer, int keyVersion, LPTSTR pszString, ULONG *pStatus)
2634 {
2635    BOOL rc = TRUE;
2636    ULONG status = 0;
2637
2638    TCHAR szCell[ cchNAME ];
2639    lpiServer->GetCellName (szCell);
2640
2641    WORKERPACKET wp;
2642    wp.wpKasStringToKey.pszCell = szCell;
2643    wp.wpKasStringToKey.pszString = pszString;
2644    if (!Worker_DoTask (wtaskKasStringToKey, &wp, &status))
2645       {
2646       rc = FALSE;
2647       }
2648    else if (!AfsClass_AddKey (lpiServer, keyVersion, &wp.wpKasStringToKey.key, &status))
2649       {
2650       rc = FALSE;
2651       }
2652
2653    if (pStatus && !rc)
2654       *pStatus = status;
2655    return rc;
2656 }
2657
2658
2659 BOOL AfsClass_AddKey (LPIDENT lpiServer, int keyVersion, LPENCRYPTIONKEY pKey, ULONG *pStatus)
2660 {
2661    BOOL rc = TRUE;
2662    ULONG status = 0;
2663
2664    PVOID hCell;
2665    PVOID hBOS;
2666    LPSERVER lpServer;
2667    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2668       rc = FALSE;
2669    else
2670       {
2671       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2672          rc = FALSE;
2673       lpServer->Close();
2674       }
2675
2676    if (rc)
2677       {
2678       WORKERPACKET wp;
2679       wp.wpBosKeyCreate.hServer = hBOS;
2680       wp.wpBosKeyCreate.keyVersion = keyVersion;
2681       memcpy (&wp.wpBosKeyCreate.key, pKey, sizeof(ENCRYPTIONKEY));
2682       rc = Worker_DoTask (wtaskBosKeyCreate, &wp, &status);
2683       }
2684
2685    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2686       {
2687       lpServer->CloseBosObject();
2688       lpServer->Close();
2689       }
2690
2691    if (pStatus && !rc)
2692       *pStatus = status;
2693    return rc;
2694 }
2695
2696
2697 BOOL AfsClass_DeleteKey (LPIDENT lpiServer, int keyVersion, ULONG *pStatus)
2698 {
2699    BOOL rc = TRUE;
2700    ULONG status = 0;
2701
2702    PVOID hCell;
2703    PVOID hBOS;
2704    LPSERVER lpServer;
2705    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
2706       rc = FALSE;
2707    else
2708       {
2709       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
2710          rc = FALSE;
2711       lpServer->Close();
2712       }
2713
2714    if (rc)
2715       {
2716       WORKERPACKET wp;
2717       wp.wpBosKeyDelete.hServer = hBOS;
2718       wp.wpBosKeyDelete.keyVersion = keyVersion;
2719       rc = Worker_DoTask (wtaskBosKeyDelete, &wp, &status);
2720       }
2721
2722    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
2723       {
2724       lpServer->CloseBosObject();
2725       lpServer->Close();
2726       }
2727
2728    if (pStatus && !rc)
2729       *pStatus = status;
2730    return rc;
2731 }
2732
2733
2734 BOOL AfsClass_GetRandomKey (LPIDENT lpi, LPENCRYPTIONKEY pKey, ULONG *pStatus)
2735 {
2736    BOOL rc = TRUE;
2737    ULONG status = 0;
2738
2739    PVOID hCell;
2740    PVOID hKAS;
2741    LPCELL lpCell;
2742    if ((lpCell = lpi->OpenCell (&status)) == NULL)
2743       rc = FALSE;
2744    else
2745       {
2746       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
2747          rc = FALSE;
2748       else
2749          hKAS = lpCell->GetKasObject (&status);
2750       lpCell->Close();
2751       }
2752
2753    if (rc)
2754       {
2755       WORKERPACKET wp;
2756       wp.wpKasServerRandomKeyGet.hCell = hCell;
2757       wp.wpKasServerRandomKeyGet.hServer = hKAS;
2758       rc = Worker_DoTask (wtaskKasServerRandomKeyGet, &wp, &status);
2759
2760       if (rc)
2761          memcpy (pKey, &wp.wpKasServerRandomKeyGet.key, sizeof(ENCRYPTIONKEY));
2762       }
2763
2764    if (pStatus && !rc)
2765       *pStatus = status;
2766    return rc;
2767 }
2768
2769
2770 BOOL AfsClass_Clone (LPIDENT lpiRW, ULONG *pStatus)
2771 {
2772    BOOL rc = TRUE;
2773    ULONG status;
2774
2775    AfsClass_Enter();
2776    NOTIFYCALLBACK::SendNotificationToAll (evtCloneBegin, lpiRW, 0);
2777
2778    // Obtain hCell
2779    //
2780    PVOID hCell;
2781    LPCELL lpCell;
2782    if ((lpCell = lpiRW->OpenCell (&status)) == NULL)
2783       rc = FALSE;
2784    else
2785       {
2786       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
2787          rc = FALSE;
2788       lpCell->Close();
2789       }
2790
2791    // Perform the actual operation
2792    //
2793    if (rc)
2794       {
2795       WORKERPACKET wp;
2796       wp.wpVosBackupVolumeCreate.hCell = hCell;
2797       lpiRW->GetFilesetID (&wp.wpVosBackupVolumeCreate.idVolume);
2798
2799       AfsClass_Leave();
2800       rc = Worker_DoTask (wtaskVosBackupVolumeCreate, &wp, &status);
2801       AfsClass_Enter();
2802       }
2803
2804    // Clean up
2805    //
2806    if (rc)
2807       {
2808       LPSERVER lpServer;
2809       if ((lpServer = lpiRW->OpenServer (&status)) == NULL)
2810          rc = FALSE;
2811       else
2812          {
2813          lpServer->Invalidate();
2814          rc = lpServer->RefreshAll (&status);
2815          lpServer->Close();
2816          }
2817       }
2818
2819    NOTIFYCALLBACK::SendNotificationToAll (evtCloneEnd, lpiRW, status);
2820    AfsClass_Leave();
2821
2822    if (pStatus && !rc)
2823       *pStatus = status;
2824    return rc;
2825 }
2826
2827
2828 BOOL AfsClass_CloneMultiple (LPIDENT lpi, LPTSTR pszPrefix, BOOL fExclude, ULONG *pStatus)
2829 {
2830    BOOL rc = TRUE;
2831    ULONG status;
2832
2833    AfsClass_Enter();
2834    NOTIFYCALLBACK::SendNotificationToAll (evtCloneMultipleBegin, lpi);
2835
2836    // Obtain hCell
2837    //
2838    PVOID hCell;
2839    LPCELL lpCell;
2840    if ((lpCell = lpi->OpenCell (&status)) == NULL)
2841       rc = FALSE;
2842    else
2843       {
2844       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
2845          rc = FALSE;
2846       lpCell->Close();
2847       }
2848
2849    // Obtain hServer if appropriate
2850    //
2851    PVOID hVOS = NULL;
2852    if (!lpi->fIsCell())
2853       {
2854       LPSERVER lpServer;
2855       if ((lpServer = lpi->OpenServer (&status)) == NULL)
2856          rc = FALSE;
2857       else
2858          {
2859          if ((hVOS = lpServer->OpenVosObject (NULL, &status)) == NULL)
2860             rc = FALSE;
2861          lpServer->Close();
2862          }
2863       }
2864
2865    // If requested, obtain the appropriate aggregate ID
2866    //
2867    int idPartition = NO_PARTITION;
2868    if (rc && (lpi->fIsFileset() || lpi->fIsAggregate()))
2869       {
2870       LPAGGREGATE lpAggregate;
2871       if ((lpAggregate = lpi->OpenAggregate (&status)) == NULL)
2872          rc = FALSE;
2873       else
2874          {
2875          if ((idPartition = lpAggregate->GetID()) == NO_PARTITION)
2876             rc = FALSE;
2877          lpAggregate->Close();
2878          }
2879       }
2880
2881    // Perform the actual operation
2882    //
2883    if (rc)
2884       {
2885       WORKERPACKET wp;
2886       wp.wpVosBackupVolumeCreateMultiple.hCell = hCell;
2887       wp.wpVosBackupVolumeCreateMultiple.hServer = hVOS;
2888       wp.wpVosBackupVolumeCreateMultiple.idPartition = idPartition;
2889       wp.wpVosBackupVolumeCreateMultiple.pszPrefix = pszPrefix;
2890       wp.wpVosBackupVolumeCreateMultiple.fExclude = fExclude;
2891
2892       AfsClass_Leave();
2893       rc = Worker_DoTask (wtaskVosBackupVolumeCreateMultiple, &wp, &status);
2894       AfsClass_Enter();
2895       }
2896
2897    // Clean up
2898    //
2899    if (rc)
2900       {
2901       if (lpi->fIsCell())
2902          {
2903          LPCELL lpCell;
2904          if ((lpCell = lpi->OpenCell (&status)) == NULL)
2905             rc = FALSE;
2906          else
2907             {
2908             lpCell->Invalidate();
2909             rc = lpCell->RefreshAll (&status);
2910             lpCell->Close();
2911             }
2912          }
2913       else
2914          {
2915          LPSERVER lpServer;
2916          if ((lpServer = lpi->OpenServer (&status)) == NULL)
2917             rc = FALSE;
2918          else
2919             {
2920             lpServer->Invalidate();
2921             rc = lpServer->RefreshAll (&status);
2922             lpServer->Close();
2923             }
2924          }
2925       }
2926
2927    if (hVOS)
2928       {
2929       LPSERVER lpServer;
2930       if ((lpServer = lpi->OpenServer (&status)) != NULL)
2931          {
2932          lpServer->CloseVosObject();
2933          lpServer->Close();
2934          }
2935       }
2936
2937    NOTIFYCALLBACK::SendNotificationToAll (evtCloneMultipleEnd, lpi, status);
2938    AfsClass_Leave();
2939
2940    if (pStatus && !rc)
2941       *pStatus = status;
2942    return rc;
2943 }
2944
2945
2946 BOOL AfsClass_DumpFileset (LPIDENT lpi, LPTSTR pszFilename, LPSYSTEMTIME pstDate, ULONG *pStatus)
2947 {
2948    BOOL rc = TRUE;
2949    ULONG status;
2950
2951    AfsClass_Enter();
2952    NOTIFYCALLBACK::SendNotificationToAll (evtDumpFilesetBegin, lpi, pszFilename, 0);
2953
2954    // Obtain hCell and hVOS
2955    //
2956    PVOID hCell;
2957    PVOID hVOS = NULL;
2958    LPSERVER lpServer;
2959    if ((lpServer = lpi->OpenServer (&status)) == NULL)
2960       rc = FALSE;
2961    else
2962       {
2963       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
2964          rc = FALSE;
2965       lpServer->Close();
2966       }
2967
2968    // Obtain idPartition
2969    //
2970    int idPartition;
2971    LPAGGREGATE lpAggregate;
2972    if ((lpAggregate = lpi->OpenAggregate (&status)) == NULL)
2973       rc = FALSE;
2974    else
2975       {
2976       idPartition = lpAggregate->GetID();
2977       lpAggregate->Close();
2978       }
2979
2980    // Perform the actual operation
2981    //
2982    if (rc)
2983       {
2984       WORKERPACKET wp;
2985       wp.wpVosVolumeDump.hCell = hCell;
2986       wp.wpVosVolumeDump.hServer = hVOS;
2987       wp.wpVosVolumeDump.pszFilename = pszFilename;
2988       wp.wpVosVolumeDump.idPartition = idPartition;
2989       lpi->GetFilesetID (&wp.wpVosVolumeDump.idVolume);
2990
2991       if (pstDate)
2992          memcpy (&wp.wpVosVolumeDump.stStart, pstDate, sizeof(SYSTEMTIME));
2993       else
2994          memset (&wp.wpVosVolumeDump.stStart, 0x00, sizeof(SYSTEMTIME));
2995
2996       AfsClass_Leave();
2997       rc = Worker_DoTask (wtaskVosVolumeDump, &wp, &status);
2998       AfsClass_Enter();
2999       }
3000
3001    NOTIFYCALLBACK::SendNotificationToAll (evtDumpFilesetEnd, lpi, pszFilename, status);
3002    AfsClass_Leave();
3003
3004    if (hVOS)
3005       {
3006       LPSERVER lpServer;
3007       if ((lpServer = lpi->OpenServer (&status)) != NULL)
3008          {
3009          lpServer->CloseVosObject();
3010          lpServer->Close();
3011          }
3012       }
3013
3014    if (pStatus && !rc)
3015       *pStatus = status;
3016    return rc;
3017 }
3018
3019
3020 BOOL AfsClass_RestoreFileset (LPIDENT lpi, LPTSTR pszFileset, LPTSTR pszFilename, BOOL fIncremental, ULONG *pStatus)
3021 {
3022    BOOL rc = TRUE;
3023    ULONG status;
3024
3025    AfsClass_Enter();
3026    NOTIFYCALLBACK::SendNotificationToAll (evtRestoreFilesetBegin, lpi, NULL, pszFileset, pszFilename, 0, 0);
3027
3028    // Obtain hCell and hVOS
3029    //
3030    PVOID hCell;
3031    PVOID hVOS = NULL;
3032    LPSERVER lpServer;
3033    if ((lpServer = lpi->OpenServer (&status)) == NULL)
3034       rc = FALSE;
3035    else
3036       {
3037       if ((hVOS = lpServer->OpenVosObject (&hCell, &status)) == NULL)
3038          rc = FALSE;
3039       lpServer->Close();
3040       }
3041
3042    // Obtain idPartition
3043    //
3044    int idPartition;
3045    LPAGGREGATE lpAggregate;
3046    if ((lpAggregate = lpi->OpenAggregate (&status)) == NULL)
3047       rc = FALSE;
3048    else
3049       {
3050       idPartition = lpAggregate->GetID();
3051       lpAggregate->Close();
3052       }
3053
3054    // Perform the actual operation
3055    //
3056    if (rc)
3057       {
3058       WORKERPACKET wp;
3059       wp.wpVosVolumeRestore.hCell = hCell;
3060       wp.wpVosVolumeRestore.hServer = hVOS;
3061       wp.wpVosVolumeRestore.idPartition = idPartition;
3062       wp.wpVosVolumeRestore.pszVolume = pszFileset;
3063       wp.wpVosVolumeRestore.pszFilename = pszFilename;
3064       wp.wpVosVolumeRestore.fIncremental = fIncremental;
3065
3066       if (lpi->fIsFileset())
3067          lpi->GetFilesetID (&wp.wpVosVolumeRestore.idVolume);
3068       else
3069          wp.wpVosVolumeRestore.idVolume = NO_VOLUME;
3070
3071       AfsClass_Leave();
3072       rc = Worker_DoTask (wtaskVosVolumeRestore, &wp, &status);
3073       AfsClass_Enter();
3074       }
3075
3076    // Clean up
3077    //
3078    if (rc)
3079       {
3080       if ((lpServer = lpi->OpenServer (&status)) == NULL)
3081          rc = FALSE;
3082       else
3083          {
3084          lpServer->Invalidate();
3085          rc = lpServer->RefreshAll (&status);
3086          lpServer->Close();
3087          }
3088       }
3089
3090    if (hVOS)
3091       {
3092       if ((lpServer = lpi->OpenServer (&status)) != NULL)
3093          {
3094          lpServer->CloseVosObject();
3095          lpServer->Close();
3096          }
3097       }
3098
3099    NOTIFYCALLBACK::SendNotificationToAll (evtRestoreFilesetEnd, lpi, NULL, pszFileset, pszFilename, 0, status);
3100    AfsClass_Leave();
3101
3102    if (pStatus && !rc)
3103       *pStatus = status;
3104    return rc;
3105 }
3106
3107
3108 BOOL AfsClass_GetRestartTimes (LPIDENT lpiServer, BOOL *pfWeekly, LPSYSTEMTIME pstWeekly, BOOL *pfDaily, LPSYSTEMTIME pstDaily, ULONG *pStatus)
3109 {
3110    BOOL rc = TRUE;
3111    ULONG status;
3112
3113    AfsClass_Enter();
3114    NOTIFYCALLBACK::SendNotificationToAll (evtGetRestartTimesBegin, lpiServer);
3115
3116    PVOID hCell;
3117    PVOID hBOS;
3118    LPSERVER lpServer;
3119    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
3120       rc = FALSE;
3121    else
3122       {
3123       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
3124          rc = FALSE;
3125       lpServer->Close();
3126       }
3127
3128    if (rc)
3129       {
3130       WORKERPACKET wp;
3131       wp.wpBosExecutableRestartTimeGet.hServer = hBOS;
3132
3133       AfsClass_Leave();
3134       rc = Worker_DoTask (wtaskBosExecutableRestartTimeGet, &wp, &status);
3135       AfsClass_Enter();
3136
3137       if (rc)
3138          {
3139          *pfWeekly = wp.wpBosExecutableRestartTimeGet.fWeeklyRestart;
3140          *pstWeekly = wp.wpBosExecutableRestartTimeGet.timeWeekly;
3141          *pfDaily = wp.wpBosExecutableRestartTimeGet.fDailyRestart;
3142          *pstDaily = wp.wpBosExecutableRestartTimeGet.timeDaily;
3143          }
3144       }
3145
3146    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
3147       {
3148       lpServer->CloseBosObject();
3149       lpServer->Close();
3150       }
3151
3152    NOTIFYCALLBACK::SendNotificationToAll (evtGetRestartTimesEnd, lpiServer, status);
3153    AfsClass_Leave();
3154
3155    if (pStatus && !rc)
3156       *pStatus = status;
3157    return rc;
3158 }
3159
3160
3161 BOOL AfsClass_SetRestartTimes (LPIDENT lpiServer, LPSYSTEMTIME pstWeekly, LPSYSTEMTIME pstDaily, ULONG *pStatus)
3162 {
3163    BOOL rc = TRUE;
3164    ULONG status;
3165
3166    AfsClass_Enter();
3167    NOTIFYCALLBACK::SendNotificationToAll (evtSetRestartTimesBegin, lpiServer);
3168
3169    PVOID hCell;
3170    PVOID hBOS;
3171    LPSERVER lpServer;
3172    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
3173       rc = FALSE;
3174    else
3175       {
3176       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
3177          rc = FALSE;
3178       lpServer->Close();
3179       }
3180
3181    if (rc)
3182       {
3183       SYSTEMTIME timeNever;
3184       memset (&timeNever, 0x00, sizeof(SYSTEMTIME));
3185
3186       WORKERPACKET wp;
3187       wp.wpBosExecutableRestartTimeSet.hServer = hBOS;
3188       wp.wpBosExecutableRestartTimeSet.fWeeklyRestart = (pstWeekly != NULL) ? TRUE : FALSE;
3189       wp.wpBosExecutableRestartTimeSet.timeWeekly = (pstWeekly != NULL) ? *pstWeekly : timeNever;
3190       wp.wpBosExecutableRestartTimeSet.fDailyRestart = (pstDaily != NULL) ? TRUE : FALSE;
3191       wp.wpBosExecutableRestartTimeSet.timeDaily = (pstDaily != NULL) ? *pstDaily : timeNever;
3192
3193       AfsClass_Leave();
3194       rc = Worker_DoTask (wtaskBosExecutableRestartTimeSet, &wp, &status);
3195       AfsClass_Enter();
3196       }
3197
3198    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
3199       {
3200       lpServer->CloseBosObject();
3201       lpServer->Close();
3202       }
3203
3204    NOTIFYCALLBACK::SendNotificationToAll (evtSetRestartTimesEnd, lpiServer, status);
3205    AfsClass_Leave();
3206
3207    if (pStatus && !rc)
3208       *pStatus = status;
3209    return rc;
3210 }
3211
3212
3213 BOOL AfsClass_MoveReplica (LPIDENT lpiReplica, LPIDENT lpiAggregateTarget, ULONG *pStatus)
3214 {
3215    BOOL rc = TRUE;
3216
3217    // Find the identifier for this replica's read/write fileset.
3218    //
3219    LPIDENT lpiFilesetRW = NULL;
3220    LPFILESET lpFileset;
3221    if ((lpFileset = lpiReplica->OpenFileset (pStatus)) == NULL)
3222       rc = FALSE;
3223    else
3224       {
3225       if ((lpiFilesetRW = lpFileset->GetReadWriteIdentifier (pStatus)) == NULL)
3226          rc = FALSE;
3227       lpFileset->Close();
3228       }
3229
3230    // If the fileset replica currently resides on the same server
3231    // as the target aggregate, we'll follow the following steps:
3232    //
3233    // 1. Delete the old fileset replica -> on error, quit
3234    // 2. Create the new fileset replica -> on error, recreate old replica, quit
3235    //
3236    // If the fileset replica instead currently resides on a different
3237    // server, we can follow the preferred steps:
3238    //
3239    // 1. Create the new fileset replica -> on error, quit
3240    // 2. Delete the old fileset replica -> on error, delete the new replica, quit
3241    //
3242    if (rc)
3243       {
3244       LPIDENT lpiReplicaNew;
3245
3246       if (lpiReplica->GetServer() == lpiAggregateTarget->GetServer())
3247          {
3248          LPIDENT lpiAggregateOriginal = lpiReplica->GetAggregate();
3249
3250          if (!AfsClass_DeleteReplica (lpiReplica, pStatus))
3251             {
3252             rc = FALSE;
3253             }
3254          else if ((lpiReplicaNew = AfsClass_CreateReplica (lpiFilesetRW, lpiAggregateTarget, pStatus)) == NULL)
3255             {
3256             (void)AfsClass_CreateReplica (lpiFilesetRW, lpiAggregateOriginal);
3257             rc = FALSE;
3258             }
3259          }
3260       else // different server?
3261          {
3262          if ((lpiReplicaNew = AfsClass_CreateReplica (lpiFilesetRW, lpiAggregateTarget, pStatus)) == NULL)
3263             {
3264             rc = FALSE;
3265             }
3266          else if (!AfsClass_DeleteReplica (lpiReplica, pStatus))
3267             {
3268             (void)AfsClass_DeleteReplica (lpiReplicaNew, pStatus);
3269             rc = FALSE;
3270             }
3271          }
3272       }
3273
3274    return rc;
3275 }
3276
3277
3278 BOOL AfsClass_Salvage (LPIDENT lpiSalvage, LPTSTR *ppszLogData, int nProcesses, LPTSTR pszTempDir, LPTSTR pszLogFile, BOOL fForce, BOOL fReadonly, BOOL fLogInodes, BOOL fLogRootInodes, BOOL fRebuildDirs, BOOL fReadBlocks, ULONG *pStatus)
3279 {
3280    BOOL rc = TRUE;
3281    ULONG status;
3282
3283    AfsClass_Enter();
3284    NOTIFYCALLBACK::SendNotificationToAll (evtSalvageBegin, lpiSalvage);
3285
3286    PVOID hCell;
3287    PVOID hBOS;
3288    LPSERVER lpServer;
3289    if ((lpServer = lpiSalvage->OpenServer (&status)) == NULL)
3290       rc = FALSE;
3291    else
3292       {
3293       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
3294          rc = FALSE;
3295       lpServer->Close();
3296       }
3297
3298    if (ppszLogData)
3299       *ppszLogData = NULL;
3300
3301    // Step one: perform the actual salvage. This will dump a log file onto
3302    // the target computer.
3303    //
3304    if (rc)
3305       {
3306       LPTSTR pszAggregate = NULL;
3307       TCHAR szAggregate[ cchNAME ];
3308       if (lpiSalvage->fIsAggregate() || lpiSalvage->fIsFileset())
3309          {
3310          lpiSalvage->GetAggregateName (szAggregate);
3311          pszAggregate = szAggregate;
3312          }
3313
3314       LPTSTR pszFileset = NULL;
3315       TCHAR szFileset[ cchNAME ];
3316       if (lpiSalvage->fIsFileset())
3317          {
3318          VOLUMEID vid;
3319          lpiSalvage->GetFilesetID (&vid);
3320          wsprintf (szFileset, TEXT("%lu"), vid);
3321          pszFileset = szFileset;
3322          }
3323
3324       if (pszLogFile == NULL)
3325          pszLogFile = TEXT("SalvageLog");
3326
3327       WORKERPACKET wp;
3328       wp.wpBosSalvage.hCell = hCell;
3329       wp.wpBosSalvage.hServer = hBOS;
3330       wp.wpBosSalvage.pszAggregate = pszAggregate;
3331       wp.wpBosSalvage.pszFileset = pszFileset;
3332       wp.wpBosSalvage.nProcesses = nProcesses;
3333       wp.wpBosSalvage.pszTempDir = pszTempDir;
3334       wp.wpBosSalvage.pszLogFile = pszLogFile;
3335       wp.wpBosSalvage.fForce = fForce;
3336       wp.wpBosSalvage.fReadonly = fReadonly;
3337       wp.wpBosSalvage.fLogInodes = fLogInodes;
3338       wp.wpBosSalvage.fLogRootInodes = fLogRootInodes;
3339       wp.wpBosSalvage.fRebuildDirs = fRebuildDirs;
3340       wp.wpBosSalvage.fReadBlocks = fReadBlocks;
3341
3342       AfsClass_Leave();
3343       rc = Worker_DoTask (wtaskBosSalvage, &wp, &status);
3344       AfsClass_Enter();
3345       }
3346
3347    // Step two: retrieve the log file from that salvage operation.
3348    // If we can't get the log file back, that's not fatal--just return
3349    // a NULL pointer for the log data.
3350    //
3351    if (rc && ppszLogData)
3352       {
3353       WORKERPACKET wp;
3354       wp.wpBosLogGet.hServer = hBOS;
3355       wp.wpBosLogGet.pszLogName = pszLogFile;
3356       wp.wpBosLogGet.pszLogData = NULL;
3357
3358       AfsClass_Leave();
3359       if ((rc = Worker_DoTask (wtaskBosLogGet, &wp, &status)) == TRUE)
3360          {
3361          // Okay, well, we have the log in memory now. Problem is,
3362          // it has UNIX-style CR's... and so is missing the LF which
3363          // PCs expect before each CR.  Wow--look at all the
3364          // acronyms!  Count the CRs, alloc a larger buffer, and stuff
3365          // in the LFs before each CR.
3366          //
3367          size_t cchRequired = 1;
3368          for (LPTSTR pchIn = wp.wpBosLogGet.pszLogData; *pchIn; ++pchIn)
3369             {
3370             cchRequired += (*pchIn == TEXT('\r')) ? 0 : (*pchIn == TEXT('\n')) ? 2 : 1;
3371             }
3372
3373          if ((*ppszLogData = AllocateString (cchRequired)) != NULL)
3374             {
3375             LPTSTR pszOut = *ppszLogData;
3376             for (LPTSTR pchIn = wp.wpBosLogGet.pszLogData; *pchIn; ++pchIn)
3377                {
3378                if (*pchIn == TEXT('\n'))
3379                   *pszOut++ = TEXT('\r');
3380                if (*pchIn != TEXT('\r'))
3381                   *pszOut++ = *pchIn;
3382                }
3383             *pszOut++ = TEXT('\0');
3384             }
3385          }
3386       AfsClass_Enter();
3387       }
3388
3389    if ((lpServer = lpiSalvage->OpenServer (&status)) != NULL)
3390       {
3391       lpServer->CloseBosObject();
3392       lpServer->Close();
3393       }
3394
3395    NOTIFYCALLBACK::SendNotificationToAll (evtSalvageEnd, lpiSalvage, status);
3396    AfsClass_Leave();
3397
3398    if (pStatus && !rc)
3399       *pStatus = status;
3400    return rc;
3401 }
3402
3403
3404 void AfsClass_FreeSalvageLog (LPTSTR pszLogData)
3405 {
3406    if (pszLogData)
3407       Free (pszLogData);
3408 }
3409
3410
3411 LPHOSTLIST AfsClass_HostList_Load (LPIDENT lpiServer, ULONG *pStatus)
3412 {
3413    BOOL rc = TRUE;
3414    ULONG status;
3415    LPHOSTLIST lpList = NULL;
3416
3417    AfsClass_Enter();
3418    NOTIFYCALLBACK::SendNotificationToAll (evtHostListLoadBegin, lpiServer);
3419
3420    PVOID hCell;
3421    PVOID hBOS;
3422    LPSERVER lpServer;
3423    if ((lpServer = lpiServer->OpenServer (&status)) == NULL)
3424       rc = FALSE;
3425    else
3426       {
3427       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
3428          rc = FALSE;
3429       lpServer->Close();
3430       }
3431
3432    if (rc)
3433       {
3434       lpList = New(HOSTLIST);
3435       memset (lpList, 0x00, sizeof(HOSTLIST));
3436       lpList->cRef = 1;
3437       lpList->lpiServer = lpiServer;
3438
3439       WORKERPACKET wpBegin;
3440       wpBegin.wpBosHostGetBegin.hServer = hBOS;
3441       if (!Worker_DoTask (wtaskBosHostGetBegin, &wpBegin, &status))
3442          rc = FALSE;
3443       else
3444          {
3445          for (;;)
3446             {
3447             TCHAR szHost[ cchNAME ];
3448
3449             WORKERPACKET wpNext;
3450             wpNext.wpBosHostGetNext.hEnum = wpBegin.wpBosHostGetBegin.hEnum;
3451             wpNext.wpBosHostGetNext.pszServer = szHost;
3452
3453             if (!Worker_DoTask (wtaskBosHostGetNext, &wpNext, &status))
3454                {
3455                if (status == ADMITERATORDONE)
3456                   status = 0;
3457                else
3458                   rc = FALSE;
3459                break;
3460                }
3461
3462             size_t iAdded;
3463             if ((iAdded = AfsClass_HostList_AddEntry (lpList, szHost)) != (size_t)-1)
3464                {
3465                lpList->aEntries[ iAdded ].fAdded = FALSE;
3466                }
3467             }
3468
3469          WORKERPACKET wpDone;
3470          wpDone.wpBosHostGetDone.hEnum = wpBegin.wpBosHostGetBegin.hEnum;
3471          Worker_DoTask (wtaskBosHostGetDone, &wpDone);
3472          }
3473       }
3474
3475    if ((lpServer = lpiServer->OpenServer (&status)) != NULL)
3476       {
3477       lpServer->CloseBosObject();
3478       lpServer->Close();
3479       }
3480
3481    NOTIFYCALLBACK::SendNotificationToAll (evtHostListLoadEnd, lpiServer, status);
3482    AfsClass_Leave();
3483
3484    if (pStatus && !rc)
3485       *pStatus = status;
3486    return (rc) ? lpList : NULL;
3487 }
3488
3489
3490 LPHOSTLIST AfsClass_HostList_Copy (LPHOSTLIST lpOld)
3491 {
3492    LPHOSTLIST lpNew = NULL;
3493
3494    if (lpOld)
3495       {
3496       lpNew = New(HOSTLIST);
3497       memcpy (lpNew, lpOld, sizeof(HOSTLIST));
3498
3499       lpNew->cRef = 1;
3500       lpNew->aEntries = 0;
3501       lpNew->cEntries = 0;
3502
3503       if (REALLOC (lpNew->aEntries, lpNew->cEntries, lpOld->cEntries, cREALLOC_HOSTLISTENTRIES))
3504          {
3505          size_t cb = lpOld->cEntries * sizeof(HOSTLISTENTRY);
3506          memcpy (lpNew->aEntries, lpOld->aEntries, cb);
3507          }
3508       }
3509
3510    return lpNew;
3511 }
3512
3513
3514 BOOL AfsClass_HostList_Save (LPHOSTLIST lpList, ULONG *pStatus)
3515 {
3516    BOOL rc = TRUE;
3517    ULONG status;
3518
3519    AfsClass_Enter();
3520    NOTIFYCALLBACK::SendNotificationToAll (evtHostListSaveBegin, lpList->lpiServer);
3521
3522    PVOID hCell;
3523    PVOID hBOS;
3524    LPSERVER lpServer;
3525    if ((lpServer = lpList->lpiServer->OpenServer (&status)) == NULL)
3526       rc = FALSE;
3527    else
3528       {
3529       if ((hBOS = lpServer->OpenBosObject (&hCell, &status)) == NULL)
3530          rc = FALSE;
3531       lpServer->Close();
3532       }
3533
3534    if (rc)
3535       {
3536       for (size_t iEntry = 0; iEntry < lpList->cEntries; ++iEntry)
3537          {
3538          if (!lpList->aEntries[ iEntry ].szHost[0])
3539             continue;
3540
3541          // are we supposed to add this entry?
3542          //
3543          if (lpList->aEntries[ iEntry ].fAdded && !lpList->aEntries[ iEntry ].fDeleted)
3544             {
3545             WORKERPACKET wp;
3546             wp.wpBosHostCreate.hServer = hBOS;
3547             wp.wpBosHostCreate.pszServer = lpList->aEntries[ iEntry ].szHost;
3548
3549             ULONG thisstatus;
3550             if (!Worker_DoTask (wtaskBosHostCreate, &wp, &thisstatus))
3551                {
3552                rc = FALSE;
3553                status = thisstatus;
3554                }
3555             else
3556                {
3557                lpList->aEntries[ iEntry ].fAdded = FALSE;
3558                }
3559             }
3560
3561          // are we supposed to delete this entry?
3562          //
3563          if (!lpList->aEntries[ iEntry ].fAdded && lpList->aEntries[ iEntry ].fDeleted)
3564             {
3565             WORKERPACKET wp;
3566             wp.wpBosHostDelete.hServer = hBOS;
3567             wp.wpBosHostDelete.pszServer = lpList->aEntries[ iEntry ].szHost;
3568
3569             ULONG thisstatus;
3570             if (!Worker_DoTask (wtaskBosHostDelete, &wp, &thisstatus))
3571                {
3572                rc = FALSE;
3573                status = thisstatus;
3574                }
3575             else
3576                {
3577                lpList->aEntries[ iEntry ].szHost[0] = TEXT('\0');
3578                lpList->aEntries[ iEntry ].fDeleted = FALSE;
3579                }
3580             }
3581          }
3582       }
3583
3584    if ((lpServer = lpList->lpiServer->OpenServer (&status)) != NULL)
3585       {
3586       lpServer->CloseBosObject();
3587       lpServer->Close();
3588       }
3589
3590    NOTIFYCALLBACK::SendNotificationToAll (evtHostListSaveEnd, lpList->lpiServer, status);
3591    AfsClass_Leave();
3592
3593    if (pStatus && !rc)
3594       *pStatus = status;
3595    return rc;
3596 }
3597
3598
3599 void AfsClass_HostList_Free (LPHOSTLIST lpList)
3600 {
3601    if (lpList && !InterlockedDecrement (&lpList->cRef))
3602       {
3603       if (lpList->aEntries)
3604          Free (lpList->aEntries);
3605       memset (lpList, 0x00, sizeof(HOSTLIST));
3606       Delete (lpList);
3607       }
3608 }
3609
3610
3611 size_t AfsClass_HostList_AddEntry (LPHOSTLIST lpList, LPTSTR pszHost)
3612 {
3613    size_t iAdded = (size_t)-1;
3614
3615    if (lpList)
3616       {
3617       size_t iEntry;
3618       for (iEntry = 0; iEntry < lpList->cEntries; ++iEntry)
3619          {
3620          if (!lpList->aEntries[ iEntry ].szHost[0])
3621             break;
3622          }
3623       if (iEntry >= lpList->cEntries)
3624          {
3625          (void)REALLOC (lpList->aEntries, lpList->cEntries, 1+iEntry, cREALLOC_HOSTLISTENTRIES);
3626          }
3627       if (iEntry < lpList->cEntries)
3628          {
3629          iAdded = iEntry;
3630          lstrcpy (lpList->aEntries[ iAdded ].szHost, pszHost);
3631          lpList->aEntries[ iAdded ].fAdded = TRUE;
3632          lpList->aEntries[ iAdded ].fDeleted = FALSE;
3633          }
3634       }
3635
3636    return iAdded;
3637 }
3638
3639
3640 BOOL AfsClass_HostList_DelEntry (LPHOSTLIST lpList, size_t iIndex)
3641 {
3642    BOOL rc = FALSE;
3643
3644    if ( lpList &&
3645         (iIndex < lpList->cEntries) &&
3646         (lpList->aEntries[ iIndex ].szHost[0]) &&
3647         (!lpList->aEntries[ iIndex ].fDeleted) )
3648       {
3649       if (lpList->aEntries[ iIndex ].fAdded)
3650          lpList->aEntries[ iIndex ].szHost[0] = TEXT('\0');
3651       else
3652          lpList->aEntries[ iIndex ].fDeleted = TRUE;
3653
3654       rc = TRUE;
3655       }
3656
3657    return rc;
3658 }
3659
3660
3661 BOOL AfsClass_GetPtsProperties (LPIDENT lpiCell, LPPTSPROPERTIES pProperties, ULONG *pStatus)
3662 {
3663    BOOL rc = TRUE;
3664    ULONG status;
3665
3666    memset (pProperties, 0x00, sizeof(PTSPROPERTIES));
3667
3668    // Obtain hCell
3669    //
3670    PVOID hCell;
3671    LPCELL lpCell;
3672    if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
3673       rc = FALSE;
3674    else
3675       {
3676       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
3677          rc = FALSE;
3678       lpCell->Close();
3679       }
3680
3681    // Go get the necessary properties
3682    //
3683    if (rc)
3684       {
3685       WORKERPACKET wp;
3686       wp.wpPtsUserMaxGet.hCell = hCell;
3687
3688       if ((rc = Worker_DoTask (wtaskPtsUserMaxGet, &wp, &status)) == TRUE)
3689          pProperties->idUserMax = wp.wpPtsUserMaxGet.idUserMax;
3690       }
3691
3692    if (rc)
3693       {
3694       WORKERPACKET wp;
3695       wp.wpPtsGroupMaxGet.hCell = hCell;
3696
3697       if ((rc = Worker_DoTask (wtaskPtsGroupMaxGet, &wp, &status)) == TRUE)
3698          pProperties->idGroupMax = wp.wpPtsGroupMaxGet.idGroupMax;
3699       }
3700
3701    if (pStatus && !rc)
3702       *pStatus = status;
3703    return rc;
3704 }
3705
3706
3707 BOOL AfsClass_SetPtsProperties (LPIDENT lpiCell, LPPTSPROPERTIES pProperties, ULONG *pStatus)
3708 {
3709    BOOL rc = TRUE;
3710    ULONG status;
3711
3712    // Obtain hCell
3713    //
3714    PVOID hCell;
3715    LPCELL lpCell;
3716    if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
3717       rc = FALSE;
3718    else
3719       {
3720       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
3721          rc = FALSE;
3722       lpCell->Close();
3723       }
3724
3725    // Modify the specified properties
3726    //
3727    if (rc)
3728       {
3729       WORKERPACKET wp;
3730       wp.wpPtsUserMaxSet.hCell = hCell;
3731       wp.wpPtsUserMaxSet.idUserMax = pProperties->idUserMax;
3732       rc = Worker_DoTask (wtaskPtsUserMaxSet, &wp, &status);
3733       }
3734
3735    if (rc)
3736       {
3737       WORKERPACKET wp;
3738       wp.wpPtsGroupMaxSet.hCell = hCell;
3739       wp.wpPtsGroupMaxSet.idGroupMax = pProperties->idGroupMax;
3740       Worker_DoTask (wtaskPtsGroupMaxSet, &wp, &status);
3741       }
3742
3743    if (pStatus && !rc)
3744       *pStatus = status;
3745    return rc;
3746 }
3747
3748
3749 LPIDENT AfsClass_CreateUser (LPIDENT lpiCell, LPTSTR pszUserName, LPTSTR pszInstance, LPTSTR pszPassword, UINT_PTR idUser, BOOL fCreateKAS, BOOL fCreatePTS, ULONG *pStatus)
3750 {
3751    BOOL rc = TRUE;
3752    ULONG status;
3753
3754    if (pszInstance && !*pszInstance)
3755       pszInstance = NULL;
3756
3757    AfsClass_Enter();
3758    NOTIFYCALLBACK::SendNotificationToAll (evtCreateUserBegin, lpiCell, pszUserName, 0);
3759
3760    // We'll need both hCell and hKAS.
3761    //
3762    PVOID hCell;
3763    PVOID hKAS;
3764    LPCELL lpCell;
3765    if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
3766       rc = FALSE;
3767    else
3768       {
3769       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
3770          rc = FALSE;
3771       else
3772          hKAS = lpCell->GetKasObject (&status);
3773       lpCell->Close();
3774       }
3775
3776    // First try to create a KAS entry.
3777    //
3778    if (rc && fCreateKAS)
3779       {
3780       WORKERPACKET wp;
3781       wp.wpKasPrincipalCreate.hCell = hCell;
3782       wp.wpKasPrincipalCreate.hServer = hKAS;
3783       wp.wpKasPrincipalCreate.pszPrincipal = pszUserName;
3784       wp.wpKasPrincipalCreate.pszInstance = pszInstance;
3785       wp.wpKasPrincipalCreate.pszPassword = pszPassword;
3786
3787       AfsClass_Leave();
3788       rc = Worker_DoTask (wtaskKasPrincipalCreate, &wp, &status);
3789       AfsClass_Enter();
3790       }
3791
3792    // If that succeeded, try to create a PTS entry as well.
3793    //
3794    if (rc && fCreatePTS)
3795       {
3796       TCHAR szUserName[ cchNAME ];
3797       lstrcpy (szUserName, pszUserName);
3798       if (pszInstance)
3799          wsprintf (&szUserName[ lstrlen(szUserName) ], TEXT(".%s"), pszInstance);
3800
3801       WORKERPACKET wp;
3802       wp.wpPtsUserCreate.hCell = hCell;
3803       wp.wpPtsUserCreate.pszUser = szUserName;
3804       wp.wpPtsUserCreate.idUser = (int) idUser;
3805
3806       AfsClass_Leave();
3807
3808       if ((rc = Worker_DoTask (wtaskPtsUserCreate, &wp, &status)) == FALSE)
3809          {
3810          if (status == PREXIST)
3811             rc = TRUE;
3812          }
3813
3814       if (!rc)
3815          {
3816          // If we couldn't make a KAS entry as well, remove the KAS entry.
3817          //
3818          if (fCreateKAS)
3819             {
3820             WORKERPACKET wpDel;
3821             wpDel.wpKasPrincipalDelete.hCell = hCell;
3822             wpDel.wpKasPrincipalDelete.hServer = hKAS;
3823             wpDel.wpKasPrincipalDelete.pszPrincipal = pszUserName;
3824             wpDel.wpKasPrincipalDelete.pszInstance = pszInstance;
3825             Worker_DoTask (wtaskKasPrincipalDelete, &wpDel);
3826             }
3827          }
3828
3829       AfsClass_Enter();
3830       }
3831
3832    // If we were able to create the user's accounts successfully, refresh
3833    // the cell status and return the new user's ident.
3834    //
3835    LPIDENT lpiUser;
3836
3837    if (rc)
3838       {
3839       if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
3840          rc = FALSE;
3841       else
3842          {
3843          if (!lpCell->RefreshAccount (pszUserName, pszInstance, CELL_REFRESH_ACCOUNT_CREATED_USER, &lpiUser))
3844             rc = FALSE;
3845          else if (!lpiUser)
3846             rc = FALSE;
3847          lpCell->Close();
3848          }
3849       }
3850
3851    NOTIFYCALLBACK::SendNotificationToAll (evtCreateUserEnd, lpiCell, pszUserName, status);
3852    AfsClass_Leave();
3853
3854    if (pStatus && !rc)
3855       *pStatus = status;
3856    return (rc) ? lpiUser : NULL;
3857 }
3858
3859
3860 BOOL AfsClass_SetUserProperties (LPIDENT lpiUser, LPUSERPROPERTIES pProperties, ULONG *pStatus)
3861 {
3862    BOOL rc = TRUE;
3863    ULONG status;
3864
3865    AfsClass_Enter();
3866    NOTIFYCALLBACK::SendNotificationToAll (evtChangeUserBegin, lpiUser);
3867
3868    // We'll need both hCell and hKAS.
3869    //
3870    PVOID hCell;
3871    PVOID hKAS;
3872    LPCELL lpCell;
3873    if ((lpCell = lpiUser->OpenCell (&status)) == NULL)
3874       rc = FALSE;
3875    else
3876       {
3877       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
3878          rc = FALSE;
3879       else
3880          hKAS = lpCell->GetKasObject (&status);
3881       lpCell->Close();
3882       }
3883
3884    // We'll also need this user's current status
3885    //
3886    LPUSER lpUser;
3887    USERSTATUS us;
3888    if ((lpUser = lpiUser->OpenUser (&status)) == NULL)
3889       rc = FALSE;
3890    else
3891       {
3892       if (!lpUser->GetStatus (&us, TRUE, &status))
3893          rc = FALSE;
3894       lpUser->Close();
3895       }
3896
3897    // Modify the user's KAS entry (if necessary)
3898    //
3899    DWORD dwKasMask = ( MASK_USERPROP_fAdmin |
3900                        MASK_USERPROP_fGrantTickets |
3901                        MASK_USERPROP_fCanEncrypt |
3902                        MASK_USERPROP_fCanChangePassword |
3903                        MASK_USERPROP_fCanReusePasswords |
3904                        MASK_USERPROP_timeAccountExpires |
3905                        MASK_USERPROP_cdayPwExpires |
3906                        MASK_USERPROP_csecTicketLifetime |
3907                        MASK_USERPROP_nFailureAttempts |
3908                        MASK_USERPROP_csecFailedLoginLockTime );
3909
3910    if (rc && (pProperties->dwMask & dwKasMask))
3911       {
3912       TCHAR szPrincipal[ cchNAME ];
3913       TCHAR szInstance[ cchNAME ];
3914       lpiUser->GetUserName (szPrincipal, szInstance);
3915
3916       WORKERPACKET wp;
3917       wp.wpKasPrincipalFieldsSet.hCell = hCell;
3918       wp.wpKasPrincipalFieldsSet.hServer = hKAS;
3919       wp.wpKasPrincipalFieldsSet.pszPrincipal = szPrincipal;
3920       wp.wpKasPrincipalFieldsSet.pszInstance = szInstance;
3921       wp.wpKasPrincipalFieldsSet.fIsAdmin = (pProperties->dwMask & MASK_USERPROP_fAdmin) ? pProperties->fAdmin : us.KASINFO.fIsAdmin;
3922       wp.wpKasPrincipalFieldsSet.fGrantTickets = (pProperties->dwMask & MASK_USERPROP_fGrantTickets) ? pProperties->fGrantTickets : us.KASINFO.fCanGetTickets;
3923       wp.wpKasPrincipalFieldsSet.fCanEncrypt = (pProperties->dwMask & MASK_USERPROP_fCanEncrypt) ? pProperties->fCanEncrypt : us.KASINFO.fEncrypt;
3924       wp.wpKasPrincipalFieldsSet.fCanChangePassword = (pProperties->dwMask & MASK_USERPROP_fCanChangePassword) ? pProperties->fCanChangePassword : us.KASINFO.fCanChangePassword;
3925       wp.wpKasPrincipalFieldsSet.fCanReusePasswords = (pProperties->dwMask & MASK_USERPROP_fCanReusePasswords) ? pProperties->fCanReusePasswords : us.KASINFO.fCanReusePasswords;
3926       memcpy (&wp.wpKasPrincipalFieldsSet.timeExpires, (pProperties->dwMask & MASK_USERPROP_timeAccountExpires) ? &pProperties->timeAccountExpires : &us.KASINFO.timeExpires, sizeof(SYSTEMTIME));
3927       wp.wpKasPrincipalFieldsSet.cdayPwExpires = (pProperties->dwMask & MASK_USERPROP_cdayPwExpires) ? pProperties->cdayPwExpires : us.KASINFO.cdayPwExpire;
3928       wp.wpKasPrincipalFieldsSet.csecTicketLifetime = (pProperties->dwMask & MASK_USERPROP_csecTicketLifetime) ? pProperties->csecTicketLifetime : us.KASINFO.csecTicketLifetime;
3929       wp.wpKasPrincipalFieldsSet.nFailureAttempts = (pProperties->dwMask & MASK_USERPROP_nFailureAttempts) ? pProperties->nFailureAttempts : us.KASINFO.cFailLogin;
3930       wp.wpKasPrincipalFieldsSet.csecFailedLoginLockTime = (pProperties->dwMask & MASK_USERPROP_csecFailedLoginLockTime) ? pProperties->csecFailedLoginLockTime : us.KASINFO.csecFailLoginLock;
3931
3932       AfsClass_Leave();
3933       rc = Worker_DoTask (wtaskKasPrincipalFieldsSet, &wp, &status);
3934       AfsClass_Enter();
3935       }
3936
3937
3938    // Modify the user's PTS entry (if necessary)
3939    //
3940    DWORD dwPtsMask = ( MASK_USERPROP_cGroupCreationQuota |
3941                        MASK_USERPROP_aaListStatus |
3942                        MASK_USERPROP_aaGroupsOwned |
3943                        MASK_USERPROP_aaMembership );
3944
3945    if (rc && (pProperties->dwMask & dwPtsMask))
3946       {
3947       TCHAR szFullName[ cchNAME ];
3948       lpiUser->GetFullUserName (szFullName);
3949
3950       WORKERPACKET wp;
3951       wp.wpPtsUserModify.hCell = hCell;
3952       wp.wpPtsUserModify.pszUser = szFullName;
3953       memset (&wp.wpPtsUserModify.Delta, 0x00, sizeof(wp.wpPtsUserModify.Delta));
3954
3955       if (pProperties->dwMask & MASK_USERPROP_cGroupCreationQuota)
3956          {
3957          wp.wpPtsUserModify.Delta.flag = (pts_UserUpdateFlag_t)( (LONG)wp.wpPtsUserModify.Delta.flag | (LONG)PTS_USER_UPDATE_GROUP_CREATE_QUOTA );
3958          wp.wpPtsUserModify.Delta.groupCreationQuota = pProperties->cGroupCreationQuota;
3959          }
3960
3961       if (pProperties->dwMask & (MASK_USERPROP_aaListStatus | MASK_USERPROP_aaGroupsOwned | MASK_USERPROP_aaMembership))
3962          {
3963          wp.wpPtsUserModify.Delta.flag = (pts_UserUpdateFlag_t)( (LONG)wp.wpPtsUserModify.Delta.flag | (LONG)PTS_USER_UPDATE_PERMISSIONS );
3964          wp.wpPtsUserModify.Delta.listStatus = ACCOUNTACCESS_TO_USERACCESS( (pProperties->dwMask & MASK_USERPROP_aaListStatus) ? pProperties->aaListStatus : us.PTSINFO.aaListStatus );
3965          wp.wpPtsUserModify.Delta.listGroupsOwned = ACCOUNTACCESS_TO_USERACCESS( (pProperties->dwMask & MASK_USERPROP_aaGroupsOwned) ? pProperties->aaGroupsOwned : us.PTSINFO.aaGroupsOwned );
3966          wp.wpPtsUserModify.Delta.listMembership = ACCOUNTACCESS_TO_USERACCESS( (pProperties->dwMask & MASK_USERPROP_aaMembership) ? pProperties->aaMembership : us.PTSINFO.aaMembership );
3967          }
3968
3969       AfsClass_Leave();
3970       rc = Worker_DoTask (wtaskPtsUserModify, &wp, &status);
3971       AfsClass_Enter();
3972       }
3973
3974    // If we were able to modify the user's properties successfully, refresh
3975    // that user's status.
3976    //
3977    if ((lpUser = lpiUser->OpenUser (&status)) != NULL)
3978       {
3979       lpUser->Invalidate();
3980       lpUser->RefreshStatus();
3981       lpUser->Close();
3982       }
3983
3984    NOTIFYCALLBACK::SendNotificationToAll (evtChangeUserBegin, lpiUser, status);
3985    AfsClass_Leave();
3986
3987    if (pStatus && !rc)
3988       *pStatus = status;
3989    return rc;
3990 }
3991
3992
3993 BOOL AfsClass_SetUserPassword (LPIDENT lpiUser, int keyVersion, LPTSTR pszPassword, ULONG *pStatus)
3994 {
3995    BOOL rc = TRUE;
3996    ULONG status = 0;
3997
3998    TCHAR szCell[ cchNAME ];
3999    lpiUser->GetCellName (szCell);
4000
4001    WORKERPACKET wp;
4002    wp.wpKasStringToKey.pszCell = szCell;
4003    wp.wpKasStringToKey.pszString = pszPassword;
4004    if (!Worker_DoTask (wtaskKasStringToKey, &wp, &status))
4005       {
4006       rc = FALSE;
4007       }
4008    else if (!AfsClass_SetUserPassword (lpiUser, keyVersion, &wp.wpKasStringToKey.key, &status))
4009       {
4010       rc = FALSE;
4011       }
4012
4013    if (pStatus && !rc)
4014       *pStatus = status;
4015    return rc;
4016 }
4017
4018
4019 BOOL AfsClass_SetUserPassword (LPIDENT lpiUser, int keyVersion, LPENCRYPTIONKEY pKey, ULONG *pStatus)
4020 {
4021    BOOL rc = TRUE;
4022    ULONG status;
4023
4024    AfsClass_Enter();
4025    NOTIFYCALLBACK::SendNotificationToAll (evtChangeUserPasswordBegin, lpiUser);
4026
4027    // We'll need both hCell and hKAS.
4028    //
4029    PVOID hCell;
4030    PVOID hKAS;
4031    LPCELL lpCell;
4032    if ((lpCell = lpiUser->OpenCell (&status)) == NULL)
4033       rc = FALSE;
4034    else
4035       {
4036       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4037          rc = FALSE;
4038       else
4039          hKAS = lpCell->GetKasObject (&status);
4040       lpCell->Close();
4041       }
4042
4043    // Change the user's password
4044    //
4045    if (rc)
4046       {
4047       TCHAR szPrincipal[ cchNAME ];
4048       TCHAR szInstance[ cchNAME ];
4049       lpiUser->GetUserName (szPrincipal, szInstance);
4050
4051       WORKERPACKET wp;
4052       wp.wpKasPrincipalKeySet.hCell = hCell;
4053       wp.wpKasPrincipalKeySet.hServer = hKAS;
4054       wp.wpKasPrincipalKeySet.pszPrincipal = szPrincipal;
4055       wp.wpKasPrincipalKeySet.pszInstance = szInstance;
4056       wp.wpKasPrincipalKeySet.keyVersion = keyVersion;
4057       memcpy (&wp.wpKasPrincipalKeySet.key.key, &pKey->key, ENCRYPTIONKEY_LEN);
4058
4059       AfsClass_Leave();
4060       rc = Worker_DoTask (wtaskKasPrincipalKeySet, &wp, &status);
4061       AfsClass_Enter();
4062       }
4063
4064    // If we were able to modify the user's password successfully, refresh
4065    // that user's status.
4066    //
4067    LPUSER lpUser;
4068    if ((lpUser = lpiUser->OpenUser (&status)) != NULL)
4069       {
4070       lpUser->Invalidate();
4071       lpUser->RefreshStatus();
4072       lpUser->Close();
4073       }
4074
4075    NOTIFYCALLBACK::SendNotificationToAll (evtChangeUserPasswordEnd, lpiUser, status);
4076    AfsClass_Leave();
4077
4078    if (pStatus && !rc)
4079       *pStatus = status;
4080    return rc;
4081 }
4082
4083
4084 BOOL AfsClass_DeleteUser (LPIDENT lpiUser, BOOL fDeleteKAS, BOOL fDeletePTS, ULONG *pStatus)
4085 {
4086    BOOL rc = TRUE;
4087    ULONG status;
4088
4089    AfsClass_Enter();
4090    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteUserBegin, lpiUser);
4091
4092    // We'll need both hCell and hKAS.
4093    //
4094    PVOID hCell;
4095    PVOID hKAS;
4096    LPCELL lpCell;
4097    if ((lpCell = lpiUser->OpenCell (&status)) == NULL)
4098       rc = FALSE;
4099    else
4100       {
4101       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4102          rc = FALSE;
4103       else
4104          hKAS = lpCell->GetKasObject (&status);
4105       lpCell->Close();
4106       }
4107
4108    // Find out whether this user has KAS and/or PTS entries. Also
4109    // get the various lists of groups for this user...
4110    //
4111    LPUSER lpUser;
4112    LPTSTR mszOwnerOf = NULL;
4113    LPTSTR mszMemberOf = NULL;
4114    if ((lpUser = lpiUser->OpenUser (&status)) == NULL)
4115       rc = FALSE;
4116    else
4117       {
4118       lpUser->GetOwnerOf (&mszOwnerOf);
4119       lpUser->GetMemberOf (&mszMemberOf);
4120       lpUser->Close();
4121       }
4122
4123    // Delete the user's PTS entry
4124    //
4125    if (rc && fDeletePTS)
4126       {
4127       TCHAR szFullName[ cchNAME ];
4128       lpiUser->GetFullUserName (szFullName);
4129
4130       WORKERPACKET wp;
4131       wp.wpPtsUserDelete.hCell = hCell;
4132       wp.wpPtsUserDelete.pszUser = szFullName;
4133
4134       AfsClass_Leave();
4135       if ((rc = Worker_DoTask (wtaskPtsUserDelete, &wp, &status)) == FALSE)
4136          {
4137          if (status == ADMPTSFAILEDNAMETRANSLATE) // User had no PTS entry?
4138             rc = TRUE;
4139          }
4140       AfsClass_Enter();
4141       }
4142
4143    // Delete the user's KAS entry
4144    //
4145    if (rc && fDeleteKAS)
4146       {
4147       TCHAR szPrincipal[ cchNAME ];
4148       TCHAR szInstance[ cchNAME ];
4149       lpiUser->GetUserName (szPrincipal, szInstance);
4150
4151       WORKERPACKET wp;
4152       wp.wpKasPrincipalDelete.hCell = hCell;
4153       wp.wpKasPrincipalDelete.hServer = hKAS;
4154       wp.wpKasPrincipalDelete.pszPrincipal = szPrincipal;
4155       wp.wpKasPrincipalDelete.pszInstance = szInstance;
4156
4157       AfsClass_Leave();
4158       if ((rc = Worker_DoTask (wtaskKasPrincipalDelete, &wp, &status)) == FALSE)
4159          {
4160          if (status == KANOENT)
4161             rc = TRUE;
4162          }
4163       AfsClass_Enter();
4164       }
4165
4166    // If we were able to delete the user's accounts successfully, refresh
4167    // the cell status.
4168    //
4169    if (rc)
4170       {
4171       if ((lpCell = lpiUser->OpenCell (&status)) != NULL)
4172          {
4173          TCHAR szPrincipal[ cchNAME ];
4174          TCHAR szInstance[ cchNAME ];
4175          lpiUser->GetUserName (szPrincipal, szInstance);
4176
4177          lpCell->RefreshAccount (szPrincipal, szInstance, CELL_REFRESH_ACCOUNT_DELETED);
4178          lpCell->RefreshAccounts (mszOwnerOf, CELL_REFRESH_ACCOUNT_CHANGED);
4179          lpCell->RefreshAccounts (mszMemberOf, CELL_REFRESH_ACCOUNT_CHANGED);
4180          lpCell->Close();
4181          }
4182       }
4183
4184    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteUserEnd, lpiUser, status);
4185    AfsClass_Leave();
4186
4187    if (mszOwnerOf)
4188       FreeString (mszOwnerOf);
4189    if (mszMemberOf)
4190       FreeString (mszMemberOf);
4191    if (pStatus && !rc)
4192       *pStatus = status;
4193    return rc;
4194 }
4195
4196
4197 BOOL AfsClass_UnlockUser (LPIDENT lpiUser, ULONG *pStatus)
4198 {
4199    BOOL rc = TRUE;
4200    ULONG status;
4201
4202    AfsClass_Enter();
4203    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockUserBegin, lpiUser);
4204
4205    // We'll need both hCell and hKAS.
4206    //
4207    PVOID hCell;
4208    PVOID hKAS;
4209    LPCELL lpCell;
4210    if ((lpCell = lpiUser->OpenCell (&status)) == NULL)
4211       rc = FALSE;
4212    else
4213       {
4214       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4215          rc = FALSE;
4216       else
4217          hKAS = lpCell->GetKasObject (&status);
4218       lpCell->Close();
4219       }
4220
4221    // Unlock the user's KAS entry
4222    //
4223    if (rc)
4224       {
4225       TCHAR szPrincipal[ cchNAME ];
4226       TCHAR szInstance[ cchNAME ];
4227       lpiUser->GetUserName (szPrincipal, szInstance);
4228
4229       WORKERPACKET wp;
4230       wp.wpKasPrincipalUnlock.hCell = hCell;
4231       wp.wpKasPrincipalUnlock.hServer = hKAS;
4232       wp.wpKasPrincipalUnlock.pszPrincipal = szPrincipal;
4233       wp.wpKasPrincipalUnlock.pszInstance = szInstance;
4234
4235       AfsClass_Leave();
4236       rc = Worker_DoTask (wtaskKasPrincipalUnlock, &wp, &status);
4237       AfsClass_Enter();
4238       }
4239
4240    // If we were able to unlock the user's accounts successfully, refresh
4241    // the user's properties.
4242    //
4243    if (rc)
4244       {
4245       LPUSER lpUser;
4246       if ((lpUser = lpiUser->OpenUser (&status)) != NULL)
4247          {
4248          lpUser->Invalidate();
4249          lpUser->RefreshStatus();
4250          lpUser->Close();
4251          }
4252       }
4253
4254    NOTIFYCALLBACK::SendNotificationToAll (evtUnlockUserEnd, lpiUser, status);
4255    AfsClass_Leave();
4256
4257    if (pStatus && !rc)
4258       *pStatus = status;
4259    return rc;
4260 }
4261
4262
4263 LPIDENT AfsClass_CreateGroup (LPIDENT lpiCell, LPTSTR pszGroupName, LPIDENT lpiOwner, int idGroup, ULONG *pStatus)
4264 {
4265    BOOL rc = TRUE;
4266    ULONG status;
4267
4268    AfsClass_Enter();
4269    NOTIFYCALLBACK::SendNotificationToAll (evtCreateGroupBegin, lpiCell, pszGroupName, 0);
4270
4271    // Obtain hCell
4272    //
4273    PVOID hCell;
4274    LPCELL lpCell;
4275    if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
4276       rc = FALSE;
4277    else
4278       {
4279       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4280          rc = FALSE;
4281       lpCell->Close();
4282       }
4283
4284    // Create a PTS entry for the new group
4285    //
4286    if (rc)
4287       {
4288       TCHAR szOwner[ cchNAME ] = TEXT("");
4289       if (lpiOwner && lpiOwner->fIsUser())
4290          lpiOwner->GetFullUserName (szOwner);
4291       else if (lpiOwner && lpiOwner->fIsGroup())
4292          lpiOwner->GetGroupName (szOwner);
4293
4294       WORKERPACKET wp;
4295       wp.wpPtsGroupCreate.hCell = hCell;
4296       wp.wpPtsGroupCreate.pszGroup = pszGroupName;
4297       wp.wpPtsGroupCreate.pszOwner = (szOwner[0]) ? szOwner : NULL;
4298       wp.wpPtsGroupCreate.idGroup = idGroup;
4299
4300       AfsClass_Leave();
4301       rc = Worker_DoTask (wtaskPtsGroupCreate, &wp, &status);
4302       AfsClass_Enter();
4303       }
4304
4305    // If we were able to create the group successfully, refresh
4306    // the cell status and return the new group's ident.
4307    //
4308    LPIDENT lpiGroup;
4309
4310    if (rc)
4311       {
4312       if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
4313          rc = FALSE;
4314       else
4315          {
4316          if (!lpCell->RefreshAccount (pszGroupName, NULL, CELL_REFRESH_ACCOUNT_CREATED_GROUP, &lpiGroup))
4317             rc = FALSE;
4318          else if (!lpiGroup)
4319             rc = FALSE;
4320          lpCell->Close();
4321          }
4322       }
4323
4324    NOTIFYCALLBACK::SendNotificationToAll (evtCreateGroupEnd, lpiCell, pszGroupName, status);
4325    AfsClass_Leave();
4326
4327    if (pStatus && !rc)
4328       *pStatus = status;
4329    return (rc) ? lpiGroup : NULL;
4330 }
4331
4332
4333 BOOL AfsClass_SetGroupProperties (LPIDENT lpiGroup, LPGROUPPROPERTIES pProperties, ULONG *pStatus)
4334 {
4335    BOOL rc = TRUE;
4336    ULONG status;
4337
4338    AfsClass_Enter();
4339    NOTIFYCALLBACK::SendNotificationToAll (evtChangeGroupBegin, lpiGroup);
4340
4341    // Obtain hCell
4342    //
4343    PVOID hCell;
4344    LPCELL lpCell;
4345    if ((lpCell = lpiGroup->OpenCell (&status)) == NULL)
4346       rc = FALSE;
4347    else
4348       {
4349       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4350          rc = FALSE;
4351       lpCell->Close();
4352       }
4353
4354    // We'll also need this group's current status
4355    //
4356    LPPTSGROUP lpGroup;
4357    PTSGROUPSTATUS gs;
4358    if ((lpGroup = lpiGroup->OpenGroup (&status)) == NULL)
4359       rc = FALSE;
4360    else
4361       {
4362       if (!lpGroup->GetStatus (&gs, TRUE, &status))
4363          rc = FALSE;
4364       lpGroup->Close();
4365       }
4366
4367    // Modify the group's PTS entry (if requested)
4368    //
4369    DWORD dwPtsMask = ( MASK_GROUPPROP_aaListStatus |
4370                        MASK_GROUPPROP_aaListGroupsOwned |
4371                        MASK_GROUPPROP_aaListMembers |
4372                        MASK_GROUPPROP_aaAddMember |
4373                        MASK_GROUPPROP_aaDeleteMember );
4374
4375    if (rc && (pProperties->dwMask & dwPtsMask))
4376       {
4377       TCHAR szGroup[ cchNAME ];
4378       lpiGroup->GetGroupName (szGroup);
4379
4380       WORKERPACKET wp;
4381       wp.wpPtsGroupModify.hCell = hCell;
4382       wp.wpPtsGroupModify.pszGroup = szGroup;
4383       memset (&wp.wpPtsGroupModify.Delta, 0x00, sizeof(wp.wpPtsGroupModify.Delta));
4384       wp.wpPtsGroupModify.Delta.listStatus = ACCOUNTACCESS_TO_GROUPACCESS( (pProperties->dwMask & MASK_GROUPPROP_aaListStatus) ? pProperties->aaListStatus : gs.aaListStatus );
4385       wp.wpPtsGroupModify.Delta.listGroupsOwned = ACCOUNTACCESS_TO_GROUPACCESS( (pProperties->dwMask & MASK_GROUPPROP_aaListGroupsOwned) ? pProperties->aaListGroupsOwned : gs.aaListGroupsOwned );
4386       wp.wpPtsGroupModify.Delta.listMembership = ACCOUNTACCESS_TO_GROUPACCESS( (pProperties->dwMask & MASK_GROUPPROP_aaListMembers) ? pProperties->aaListMembers : gs.aaListMembers );
4387       wp.wpPtsGroupModify.Delta.listAdd = ACCOUNTACCESS_TO_GROUPACCESS( (pProperties->dwMask & MASK_GROUPPROP_aaAddMember) ? pProperties->aaAddMember : gs.aaAddMember );
4388       wp.wpPtsGroupModify.Delta.listDelete = ACCOUNTACCESS_TO_GROUPACCESS( (pProperties->dwMask & MASK_GROUPPROP_aaDeleteMember) ? pProperties->aaDeleteMember : gs.aaDeleteMember );
4389
4390       AfsClass_Leave();
4391       rc = Worker_DoTask (wtaskPtsGroupModify, &wp, &status);
4392       AfsClass_Enter();
4393       }
4394
4395    // Change the group's owner (if requested)
4396    //
4397    if (rc && (pProperties->dwMask & MASK_GROUPPROP_szOwner))
4398       {
4399       TCHAR szGroup[ cchNAME ];
4400       lpiGroup->GetGroupName (szGroup);
4401
4402       WORKERPACKET wp;
4403       wp.wpPtsGroupOwnerChange.hCell = hCell;
4404       wp.wpPtsGroupOwnerChange.pszGroup = szGroup;
4405       wp.wpPtsGroupOwnerChange.pszOwner = pProperties->szOwner;
4406
4407       AfsClass_Leave();
4408       rc = Worker_DoTask (wtaskPtsGroupOwnerChange, &wp, &status);
4409       AfsClass_Enter();
4410       }
4411
4412    // If we were able to modify the group's properties successfully, refresh
4413    // either the group's status. If the group's owner changed, also refresh
4414    // the group's old and new owners.
4415    //
4416    if (rc)
4417       {
4418       if ((lpCell = lpiGroup->OpenCell (&status)) != NULL)
4419          {
4420          TCHAR szAccount[ cchNAME ];
4421          lpiGroup->GetGroupName (szAccount);
4422          lpCell->RefreshAccount (szAccount, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4423
4424          if (pProperties->dwMask & MASK_GROUPPROP_szOwner)
4425             {
4426             lpCell->RefreshAccount (gs.szOwner, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4427             lpCell->RefreshAccount (pProperties->szOwner, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4428             }
4429
4430          lpCell->Close();
4431          }
4432       }
4433
4434    NOTIFYCALLBACK::SendNotificationToAll (evtChangeGroupBegin, lpiGroup, status);
4435    AfsClass_Leave();
4436
4437    if (pStatus && !rc)
4438       *pStatus = status;
4439    return rc;
4440 }
4441
4442
4443 BOOL AfsClass_RenameGroup (LPIDENT lpiGroup, LPTSTR pszNewName, ULONG *pStatus)
4444 {
4445    BOOL rc = TRUE;
4446    ULONG status;
4447
4448    AfsClass_Enter();
4449    NOTIFYCALLBACK::SendNotificationToAll (evtRenameGroupBegin, lpiGroup);
4450
4451    // Obtain hCell
4452    //
4453    PVOID hCell;
4454    LPCELL lpCell;
4455    if ((lpCell = lpiGroup->OpenCell (&status)) == NULL)
4456       rc = FALSE;
4457    else
4458       {
4459       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4460          rc = FALSE;
4461       lpCell->Close();
4462       }
4463
4464    // Get this group's list of members (etc)
4465    //
4466    LPTSTR mszOwnerOf = NULL;
4467    LPTSTR mszMemberOf = NULL;
4468    LPTSTR mszMembers = NULL;
4469
4470    LPPTSGROUP lpGroup;
4471    if ((lpGroup = lpiGroup->OpenGroup (&status)) != NULL)
4472       {
4473       lpGroup->GetOwnerOf (&mszOwnerOf);
4474       lpGroup->GetMemberOf (&mszMemberOf);
4475       lpGroup->GetMembers (&mszMembers);
4476       lpGroup->Close();
4477       }
4478
4479    // Rename the group's PTS entry
4480    //
4481    if (rc)
4482       {
4483       TCHAR szGroup[ cchNAME ];
4484       lpiGroup->GetGroupName (szGroup);
4485
4486       WORKERPACKET wp;
4487       wp.wpPtsGroupRename.hCell = hCell;
4488       wp.wpPtsGroupRename.pszGroup = szGroup;
4489       wp.wpPtsGroupRename.pszNewName = pszNewName;
4490
4491       AfsClass_Leave();
4492       rc = Worker_DoTask (wtaskPtsGroupRename, &wp, &status);
4493       AfsClass_Enter();
4494       }
4495
4496    // If we were able to rename the group successfully, refresh the cell status.
4497    //
4498    if (rc)
4499       {
4500       LPPTSGROUP lpGroup;
4501       if ((lpGroup = lpiGroup->OpenGroup (&status)) != NULL)
4502          {
4503          lpGroup->ChangeIdentName (pszNewName);
4504          lpGroup->Close();
4505          }
4506       if ((lpCell = lpiGroup->OpenCell (&status)) != NULL)
4507          {
4508          lpCell->RefreshAccount (pszNewName, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4509          lpCell->RefreshAccounts (mszOwnerOf, CELL_REFRESH_ACCOUNT_CHANGED);
4510          lpCell->RefreshAccounts (mszMemberOf, CELL_REFRESH_ACCOUNT_CHANGED);
4511          lpCell->RefreshAccounts (mszMembers, CELL_REFRESH_ACCOUNT_CHANGED);
4512          lpCell->Close();
4513          }
4514       }
4515
4516    NOTIFYCALLBACK::SendNotificationToAll (evtRenameGroupEnd, lpiGroup, status);
4517    AfsClass_Leave();
4518
4519    if (mszOwnerOf)
4520       FreeString (mszOwnerOf);
4521    if (mszMemberOf)
4522       FreeString (mszMemberOf);
4523    if (mszMembers)
4524       FreeString (mszMembers);
4525    if (pStatus && !rc)
4526       *pStatus = status;
4527    return rc;
4528 }
4529
4530
4531 BOOL AfsClass_DeleteGroup (LPIDENT lpiGroup, ULONG *pStatus)
4532 {
4533    BOOL rc = TRUE;
4534    ULONG status;
4535
4536    AfsClass_Enter();
4537    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteGroupBegin, lpiGroup);
4538
4539    // Obtain hCell
4540    //
4541    PVOID hCell;
4542    LPCELL lpCell;
4543    if ((lpCell = lpiGroup->OpenCell (&status)) == NULL)
4544       rc = FALSE;
4545    else
4546       {
4547       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4548          rc = FALSE;
4549       lpCell->Close();
4550       }
4551
4552    // Get this group's list of members (etc)
4553    //
4554    LPTSTR mszOwnerOf = NULL;
4555    LPTSTR mszMemberOf = NULL;
4556    LPTSTR mszMembers = NULL;
4557
4558    LPPTSGROUP lpGroup;
4559    if ((lpGroup = lpiGroup->OpenGroup (&status)) != NULL)
4560       {
4561       lpGroup->GetOwnerOf (&mszOwnerOf);
4562       lpGroup->GetMemberOf (&mszMemberOf);
4563       lpGroup->GetMembers (&mszMembers);
4564       lpGroup->Close();
4565       }
4566
4567    // Delete the group's PTS entry
4568    //
4569    if (rc)
4570       {
4571       TCHAR szGroup[ cchNAME ];
4572       lpiGroup->GetGroupName (szGroup);
4573
4574       WORKERPACKET wp;
4575       wp.wpPtsGroupDelete.hCell = hCell;
4576       wp.wpPtsGroupDelete.pszGroup = szGroup;
4577
4578       AfsClass_Leave();
4579       if ((rc = Worker_DoTask (wtaskPtsGroupDelete, &wp, &status)) == FALSE)
4580          {
4581          if (status == ADMPTSFAILEDNAMETRANSLATE) // Group had no PTS entry?
4582             rc = TRUE;
4583          }
4584       AfsClass_Enter();
4585       }
4586
4587    // If we were able to delete the group successfully, refresh the cell status.
4588    //
4589    if (rc)
4590       {
4591       if ((lpCell = lpiGroup->OpenCell (&status)) != NULL)
4592          {
4593          TCHAR szGroup[ cchNAME ];
4594          lpiGroup->GetGroupName (szGroup);
4595          lpCell->RefreshAccounts (mszOwnerOf, CELL_REFRESH_ACCOUNT_CHANGED);
4596          lpCell->RefreshAccounts (mszMemberOf, CELL_REFRESH_ACCOUNT_CHANGED);
4597          lpCell->RefreshAccounts (mszMembers, CELL_REFRESH_ACCOUNT_CHANGED);
4598          lpCell->RefreshAccount (szGroup, NULL, CELL_REFRESH_ACCOUNT_DELETED);
4599          lpCell->Close();
4600          }
4601       }
4602
4603    NOTIFYCALLBACK::SendNotificationToAll (evtDeleteGroupEnd, lpiGroup, status);
4604    AfsClass_Leave();
4605
4606    if (mszOwnerOf)
4607       FreeString (mszOwnerOf);
4608    if (mszMemberOf)
4609       FreeString (mszMemberOf);
4610    if (mszMembers)
4611       FreeString (mszMembers);
4612    if (pStatus && !rc)
4613       *pStatus = status;
4614    return rc;
4615 }
4616
4617
4618 BOOL AfsClass_AddUserToGroup (LPIDENT lpiGroup, LPIDENT lpiUser, ULONG *pStatus)
4619 {
4620    BOOL rc = TRUE;
4621    ULONG status;
4622
4623    AfsClass_Enter();
4624    NOTIFYCALLBACK::SendNotificationToAll (evtGroupMemberAddBegin, lpiGroup, lpiUser, NULL, NULL, 0, 0);
4625
4626    // Obtain hCell
4627    //
4628    PVOID hCell;
4629    LPCELL lpCell;
4630    if ((lpCell = lpiGroup->OpenCell (&status)) == NULL)
4631       rc = FALSE;
4632    else
4633       {
4634       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4635          rc = FALSE;
4636       lpCell->Close();
4637       }
4638
4639    TCHAR szGroup[ cchNAME ];
4640    lpiGroup->GetGroupName (szGroup);
4641
4642    TCHAR szMember[ cchNAME ];
4643    if (lpiUser->fIsUser())
4644       lpiUser->GetFullUserName (szMember);
4645    else // (lpiUser->fIsGroup())
4646       lpiUser->GetGroupName (szMember);
4647
4648    // Add this user to the specified group
4649    //
4650    if (rc)
4651       {
4652       WORKERPACKET wp;
4653       wp.wpPtsGroupMemberAdd.hCell = hCell;
4654       wp.wpPtsGroupMemberAdd.pszGroup = szGroup;
4655       wp.wpPtsGroupMemberAdd.pszUser = szMember;
4656
4657       AfsClass_Leave();
4658       rc = Worker_DoTask (wtaskPtsGroupMemberAdd, &wp, &status);
4659       AfsClass_Enter();
4660       }
4661
4662    // If we were able to change the group successfully, update the group's
4663    // and user's properties.
4664    //
4665    if (rc)
4666       {
4667       if ((lpCell = lpiGroup->OpenCell (&status)) != NULL)
4668          {
4669          lpCell->RefreshAccount (szGroup, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4670          lpCell->RefreshAccount (szMember, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4671          lpCell->Close();
4672          }
4673       }
4674
4675    NOTIFYCALLBACK::SendNotificationToAll (evtGroupMemberAddEnd, lpiGroup, lpiUser, NULL, NULL, 0, status);
4676    AfsClass_Leave();
4677
4678    if (pStatus && !rc)
4679       *pStatus = status;
4680    return rc;
4681 }
4682
4683
4684 BOOL AfsClass_RemoveUserFromGroup (LPIDENT lpiGroup, LPIDENT lpiUser, ULONG *pStatus)
4685 {
4686    BOOL rc = TRUE;
4687    ULONG status;
4688
4689    AfsClass_Enter();
4690    NOTIFYCALLBACK::SendNotificationToAll (evtGroupMemberRemoveBegin, lpiGroup, lpiUser, NULL, NULL, 0, 0);
4691
4692    // Obtain hCell
4693    //
4694    PVOID hCell;
4695    LPCELL lpCell;
4696    if ((lpCell = lpiGroup->OpenCell (&status)) == NULL)
4697       rc = FALSE;
4698    else
4699       {
4700       if ((hCell = lpCell->GetCellObject (&status)) == NULL)
4701          rc = FALSE;
4702       lpCell->Close();
4703       }
4704
4705    TCHAR szGroup[ cchNAME ];
4706    lpiGroup->GetGroupName (szGroup);
4707
4708    TCHAR szMember[ cchNAME ];
4709    if (lpiUser->fIsUser())
4710       lpiUser->GetFullUserName (szMember);
4711    else // (lpiUser->fIsGroup())
4712       lpiUser->GetGroupName (szMember);
4713
4714    // Remove this user from the specified group
4715    //
4716    if (rc)
4717       {
4718       WORKERPACKET wp;
4719       wp.wpPtsGroupMemberRemove.hCell = hCell;
4720       wp.wpPtsGroupMemberRemove.pszGroup = szGroup;
4721       wp.wpPtsGroupMemberRemove.pszUser = szMember;
4722
4723       AfsClass_Leave();
4724       rc = Worker_DoTask (wtaskPtsGroupMemberRemove, &wp, &status);
4725       AfsClass_Enter();
4726       }
4727
4728    // If we were able to change the group successfully, update the group's
4729    // and user's properties.
4730    //
4731    if (rc)
4732       {
4733       if ((lpCell = lpiGroup->OpenCell (&status)) != NULL)
4734          {
4735          lpCell->RefreshAccount (szGroup, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4736          lpCell->RefreshAccount (szMember, NULL, CELL_REFRESH_ACCOUNT_CHANGED);
4737          lpCell->Close();
4738          }
4739       }
4740
4741    NOTIFYCALLBACK::SendNotificationToAll (evtGroupMemberRemoveEnd, lpiGroup, lpiUser, NULL, NULL, 0, status);
4742    AfsClass_Leave();
4743
4744    if (pStatus && !rc)
4745       *pStatus = status;
4746    return rc;
4747 }
4748