r7981: MS-DFS tidyup patches from James Peach <jpeach@sgi.com>.
[abartlet/samba.git/.git] / source / rpc_server / srv_dfs_nt.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines for Dfs
4  *  Copyright (C) Andrew Tridgell              1992-1997,
5  *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
6  *  Copyright (C) Shirish Kalele               2000.
7  *  Copyright (C) Jeremy Allison                                2001.
8  *  
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *  
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *  
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 /* This is the implementation of the dfs pipe. */
25
26 #include "includes.h"
27 #include "nterr.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_MSDFS
31
32 /* This function does not return a WERROR or NTSTATUS code but rather 1 if
33    dfs exists, or 0 otherwise. */
34
35 uint32 _dfs_exist(pipes_struct *p, DFS_Q_DFS_EXIST *q_u, DFS_R_DFS_EXIST *r_u)
36 {
37         if(lp_host_msdfs()) 
38                 return 1;
39         else
40                 return 0;
41 }
42
43 WERROR _dfs_add(pipes_struct *p, DFS_Q_DFS_ADD* q_u, DFS_R_DFS_ADD *r_u)
44 {
45         struct current_user user;
46         struct junction_map jn;
47         struct referral* old_referral_list = NULL;
48         BOOL exists = False;
49
50         pstring dfspath, servername, sharename;
51         pstring altpath;
52
53         get_current_user(&user,p);
54
55         if (user.uid != 0) {
56                 DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
57                 return WERR_ACCESS_DENIED;
58         }
59
60         unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
61         unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
62         unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
63
64         DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n",
65                 dfspath, servername, sharename));
66
67         pstrcpy(altpath, servername);
68         pstrcat(altpath, "\\");
69         pstrcat(altpath, sharename);
70
71         /* The following call can change the cwd. */
72         if(get_referred_path(dfspath, &jn, NULL, NULL)) {
73                 exists = True;
74                 jn.referral_count += 1;
75                 old_referral_list = jn.referral_list;
76         } else {
77                 jn.referral_count = 1;
78         }
79
80         vfs_ChDir(p->conn,p->conn->connectpath);
81
82         jn.referral_list = TALLOC_ARRAY(p->mem_ctx, struct referral, jn.referral_count);
83         if(jn.referral_list == NULL) {
84                 DEBUG(0,("init_reply_dfs_add: talloc failed for referral list!\n"));
85                 return WERR_DFS_INTERNAL_ERROR;
86         }
87
88         if(old_referral_list) {
89                 memcpy(jn.referral_list, old_referral_list, sizeof(struct referral)*jn.referral_count-1);
90                 SAFE_FREE(old_referral_list);
91         }
92   
93         jn.referral_list[jn.referral_count-1].proximity = 0;
94         jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL;
95
96         pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath);
97   
98         if(!create_msdfs_link(&jn, exists)) {
99                 vfs_ChDir(p->conn,p->conn->connectpath);
100                 return WERR_DFS_CANT_CREATE_JUNCT;
101         }
102         vfs_ChDir(p->conn,p->conn->connectpath);
103
104         return WERR_OK;
105 }
106
107 WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u, 
108                    DFS_R_DFS_REMOVE *r_u)
109 {
110         struct current_user user;
111         struct junction_map jn;
112         BOOL found = False;
113
114         pstring dfspath, servername, sharename;
115         pstring altpath;
116
117         get_current_user(&user,p);
118
119         if (user.uid != 0) {
120                 DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
121                 return WERR_ACCESS_DENIED;
122         }
123
124         unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
125         if(q_u->ptr_ServerName) {
126                 unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
127         }
128
129         if(q_u->ptr_ShareName) {
130                 unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
131         }
132
133         if(q_u->ptr_ServerName && q_u->ptr_ShareName) {
134                 pstrcpy(altpath, servername);
135                 pstrcat(altpath, "\\");
136                 pstrcat(altpath, sharename);
137                 strlower_m(altpath);
138         }
139
140         DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
141                 dfspath, servername, sharename));
142
143         if(!get_referred_path(dfspath, &jn, NULL, NULL)) {
144                 return WERR_DFS_NO_SUCH_VOL;
145         }
146
147         /* if no server-share pair given, remove the msdfs link completely */
148         if(!q_u->ptr_ServerName && !q_u->ptr_ShareName) {
149                 if(!remove_msdfs_link(&jn)) {
150                         vfs_ChDir(p->conn,p->conn->connectpath);
151                         return WERR_DFS_NO_SUCH_VOL;
152                 }
153                 vfs_ChDir(p->conn,p->conn->connectpath);
154         } else {
155                 int i=0;
156                 /* compare each referral in the list with the one to remove */
157                 DEBUG(10,("altpath: .%s. refcnt: %d\n", altpath, jn.referral_count));
158                 for(i=0;i<jn.referral_count;i++) {
159                         pstring refpath;
160                         pstrcpy(refpath,jn.referral_list[i].alternate_path);
161                         trim_char(refpath, '\\', '\\');
162                         DEBUG(10,("_dfs_remove:  refpath: .%s.\n", refpath));
163                         if(strequal(refpath, altpath)) {
164                                 *(jn.referral_list[i].alternate_path)='\0';
165                                 DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
166                                         refpath));
167                                 found = True;
168                         }
169                 }
170
171                 if(!found) {
172                         return WERR_DFS_NO_SUCH_SHARE;
173                 }
174
175                 /* Only one referral, remove it */
176                 if(jn.referral_count == 1) {
177                         if(!remove_msdfs_link(&jn)) {
178                                 vfs_ChDir(p->conn,p->conn->connectpath);
179                                 return WERR_DFS_NO_SUCH_VOL;
180                         }
181                 } else {
182                         if(!create_msdfs_link(&jn, True)) { 
183                                 vfs_ChDir(p->conn,p->conn->connectpath);
184                                 return WERR_DFS_CANT_CREATE_JUNCT;
185                         }
186                 }
187                 vfs_ChDir(p->conn,p->conn->connectpath);
188         }
189
190         return WERR_OK;
191 }
192
193 static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j)
194 {
195         int i=0;
196         for(i=0;i<num_j;i++) {
197                 pstring str;
198                 dfs1[i].ptr_entrypath = 1;
199                 slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(), 
200                         j[i].service_name, j[i].volume_name);
201                 DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str));
202                 init_unistr2(&dfs1[i].entrypath,str,UNI_STR_TERMINATE);
203         }
204         return True;
205 }
206
207 static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j)
208 {
209         int i=0;
210         for(i=0;i<num_j;i++) {
211                 pstring str;
212                 dfs2[i].ptr_entrypath = 1;
213                 slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
214                         j[i].service_name, j[i].volume_name);
215                 init_unistr2(&dfs2[i].entrypath, str, UNI_STR_TERMINATE);
216                 dfs2[i].ptr_comment = 0;
217                 dfs2[i].state = 1; /* set up state of dfs junction as OK */
218                 dfs2[i].num_storages = j[i].referral_count;
219         }
220         return True;
221 }
222
223 static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j)
224 {
225         int i=0,ii=0;
226         for(i=0;i<num_j;i++) {
227                 pstring str;
228                 dfs3[i].ptr_entrypath = 1;
229                 if (j[i].volume_name[0] == '\0')
230                         slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s",
231                                 global_myname(), j[i].service_name);
232                 else
233                         slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
234                                 j[i].service_name, j[i].volume_name);
235
236                 init_unistr2(&dfs3[i].entrypath, str, UNI_STR_TERMINATE);
237                 dfs3[i].ptr_comment = 1;
238                 init_unistr2(&dfs3[i].comment, "", UNI_STR_TERMINATE);
239                 dfs3[i].state = 1;
240                 dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count;
241                 dfs3[i].ptr_storages = 1;
242      
243                 /* also enumerate the storages */
244                 dfs3[i].storages = TALLOC_ARRAY(ctx, DFS_STORAGE_INFO, j[i].referral_count);
245                 if (!dfs3[i].storages)
246                         return False;
247
248                 memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO));
249
250                 for(ii=0;ii<j[i].referral_count;ii++) {
251                         char* p; 
252                         pstring path;
253                         DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]);
254                         struct referral* ref = &(j[i].referral_list[ii]);
255           
256                         pstrcpy(path, ref->alternate_path);
257                         trim_char(path,'\\','\0');
258                         p = strrchr_m(path,'\\');
259                         if(p==NULL) {
260                                 DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
261                                 continue;
262                         }
263                         *p = '\0';
264                         DEBUG(5,("storage %d: %s.%s\n",ii,path,p+1));
265                         stor->state = 2; /* set all storages as ONLINE */
266                         init_unistr2(&stor->servername, path, UNI_STR_TERMINATE);
267                         init_unistr2(&stor->sharename,  p+1, UNI_STR_TERMINATE);
268                         stor->ptr_servername = stor->ptr_sharename = 1;
269                 }
270         }
271         return True;
272 }
273
274 static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level, 
275                                    DFS_INFO_CTR* ctr, struct junction_map* jn,
276                                    int num_jn)
277 {
278         /* do the levels */
279         switch(level) {
280         case 1:
281                 {
282                 DFS_INFO_1* dfs1;
283                 dfs1 = TALLOC_ARRAY(ctx, DFS_INFO_1, num_jn);
284                 if (!dfs1)
285                         return WERR_NOMEM;
286                 init_reply_dfs_info_1(jn, dfs1, num_jn);
287                 ctr->dfs.info1 = dfs1;
288                 break;
289                 }
290         case 2:
291                 {
292                 DFS_INFO_2* dfs2;
293                 dfs2 = TALLOC_ARRAY(ctx, DFS_INFO_2, num_jn);
294                 if (!dfs2)
295                         return WERR_NOMEM;
296                 init_reply_dfs_info_2(jn, dfs2, num_jn);
297                 ctr->dfs.info2 = dfs2;
298                 break;
299                 }
300         case 3:
301                 {
302                 DFS_INFO_3* dfs3;
303                 dfs3 = TALLOC_ARRAY(ctx, DFS_INFO_3, num_jn);
304                 if (!dfs3)
305                         return WERR_NOMEM;
306                 init_reply_dfs_info_3(ctx, jn, dfs3, num_jn);
307                 ctr->dfs.info3 = dfs3;
308                 break;
309                 }
310         default:
311                 return WERR_INVALID_PARAM;
312         }
313         return WERR_OK;
314 }
315       
316 WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u)
317 {
318         uint32 level = q_u->level;
319         struct junction_map jn[MAX_MSDFS_JUNCTIONS];
320         int num_jn = 0;
321
322         num_jn = enum_msdfs_links(jn, ARRAY_SIZE(jn));
323         vfs_ChDir(p->conn,p->conn->connectpath);
324     
325         DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level));
326
327         r_u->ptr_buffer = level;
328         r_u->level = r_u->level2 = level;
329         r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1;
330         r_u->num_entries = r_u->num_entries2 = num_jn;
331         r_u->reshnd.ptr_hnd = 1;
332         r_u->reshnd.handle = num_jn;
333   
334         r_u->ctr = TALLOC_P(p->mem_ctx, DFS_INFO_CTR);
335         if (!r_u->ctr)
336                 return WERR_NOMEM;
337         ZERO_STRUCTP(r_u->ctr);
338         r_u->ctr->switch_value = level;
339         r_u->ctr->num_entries = num_jn;
340         r_u->ctr->ptr_dfs_ctr = 1;
341   
342         r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn);
343
344         return r_u->status;
345 }
346       
347 WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u, 
348                      DFS_R_DFS_GET_INFO *r_u)
349 {
350         UNISTR2* uni_path = &q_u->uni_path;
351         uint32 level = q_u->level;
352         int consumedcnt = sizeof(pstring);
353         pstring path;
354         struct junction_map jn;
355
356         unistr2_to_ascii(path, uni_path, sizeof(path)-1);
357         if(!create_junction(path, &jn))
358                 return WERR_DFS_NO_SUCH_SERVER;
359   
360         /* The following call can change the cwd. */
361         if(!get_referred_path(path, &jn, &consumedcnt, NULL) || consumedcnt < strlen(path)) {
362                 vfs_ChDir(p->conn,p->conn->connectpath);
363                 return WERR_DFS_NO_SUCH_VOL;
364         }
365
366         vfs_ChDir(p->conn,p->conn->connectpath);
367         r_u->level = level;
368         r_u->ptr_ctr = 1;
369         r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1);
370   
371         return r_u->status;
372 }