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