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