Initial import
[samba] / 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(p->mem_ctx, 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         }
91   
92         jn.referral_list[jn.referral_count-1].proximity = 0;
93         jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL;
94
95         pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath);
96   
97         if(!create_msdfs_link(&jn, exists)) {
98                 vfs_ChDir(p->conn,p->conn->connectpath);
99                 return WERR_DFS_CANT_CREATE_JUNCT;
100         }
101         vfs_ChDir(p->conn,p->conn->connectpath);
102
103         return WERR_OK;
104 }
105
106 WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u, 
107                    DFS_R_DFS_REMOVE *r_u)
108 {
109         struct current_user user;
110         struct junction_map jn;
111         BOOL found = False;
112
113         pstring dfspath, servername, sharename;
114         pstring altpath;
115
116         get_current_user(&user,p);
117
118         if (user.uid != 0) {
119                 DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
120                 return WERR_ACCESS_DENIED;
121         }
122
123         unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
124         if(q_u->ptr_ServerName) {
125                 unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
126         }
127
128         if(q_u->ptr_ShareName) {
129                 unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
130         }
131
132         if(q_u->ptr_ServerName && q_u->ptr_ShareName) {
133                 pstrcpy(altpath, servername);
134                 pstrcat(altpath, "\\");
135                 pstrcat(altpath, sharename);
136                 strlower_m(altpath);
137         }
138
139         DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
140                 dfspath, servername, sharename));
141
142         if(!get_referred_path(p->mem_ctx, dfspath, &jn, NULL, NULL)) {
143                 return WERR_DFS_NO_SUCH_VOL;
144         }
145
146         /* if no server-share pair given, remove the msdfs link completely */
147         if(!q_u->ptr_ServerName && !q_u->ptr_ShareName) {
148                 if(!remove_msdfs_link(&jn)) {
149                         vfs_ChDir(p->conn,p->conn->connectpath);
150                         return WERR_DFS_NO_SUCH_VOL;
151                 }
152                 vfs_ChDir(p->conn,p->conn->connectpath);
153         } else {
154                 int i=0;
155                 /* compare each referral in the list with the one to remove */
156                 DEBUG(10,("altpath: .%s. refcnt: %d\n", altpath, jn.referral_count));
157                 for(i=0;i<jn.referral_count;i++) {
158                         pstring refpath;
159                         pstrcpy(refpath,jn.referral_list[i].alternate_path);
160                         trim_char(refpath, '\\', '\\');
161                         DEBUG(10,("_dfs_remove:  refpath: .%s.\n", refpath));
162                         if(strequal(refpath, altpath)) {
163                                 *(jn.referral_list[i].alternate_path)='\0';
164                                 DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
165                                         refpath));
166                                 found = True;
167                         }
168                 }
169
170                 if(!found) {
171                         return WERR_DFS_NO_SUCH_SHARE;
172                 }
173
174                 /* Only one referral, remove it */
175                 if(jn.referral_count == 1) {
176                         if(!remove_msdfs_link(&jn)) {
177                                 vfs_ChDir(p->conn,p->conn->connectpath);
178                                 return WERR_DFS_NO_SUCH_VOL;
179                         }
180                 } else {
181                         if(!create_msdfs_link(&jn, True)) { 
182                                 vfs_ChDir(p->conn,p->conn->connectpath);
183                                 return WERR_DFS_CANT_CREATE_JUNCT;
184                         }
185                 }
186                 vfs_ChDir(p->conn,p->conn->connectpath);
187         }
188
189         return WERR_OK;
190 }
191
192 static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j)
193 {
194         int i=0;
195         for(i=0;i<num_j;i++) {
196                 pstring str;
197                 dfs1[i].ptr_entrypath = 1;
198                 slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(), 
199                         j[i].service_name, j[i].volume_name);
200                 DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str));
201                 init_unistr2(&dfs1[i].entrypath,str,UNI_STR_TERMINATE);
202         }
203         return True;
204 }
205
206 static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j)
207 {
208         int i=0;
209         for(i=0;i<num_j;i++) {
210                 pstring str;
211                 dfs2[i].ptr_entrypath = 1;
212                 slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
213                         j[i].service_name, j[i].volume_name);
214                 init_unistr2(&dfs2[i].entrypath, str, UNI_STR_TERMINATE);
215                 dfs2[i].ptr_comment = 0;
216                 dfs2[i].state = 1; /* set up state of dfs junction as OK */
217                 dfs2[i].num_storages = j[i].referral_count;
218         }
219         return True;
220 }
221
222 static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j)
223 {
224         int i=0,ii=0;
225         for(i=0;i<num_j;i++) {
226                 pstring str;
227                 dfs3[i].ptr_entrypath = 1;
228                 if (j[i].volume_name[0] == '\0')
229                         slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s",
230                                 global_myname(), j[i].service_name);
231                 else
232                         slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
233                                 j[i].service_name, j[i].volume_name);
234
235                 init_unistr2(&dfs3[i].entrypath, str, UNI_STR_TERMINATE);
236                 dfs3[i].ptr_comment = 1;
237                 init_unistr2(&dfs3[i].comment, "", UNI_STR_TERMINATE);
238                 dfs3[i].state = 1;
239                 dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count;
240                 dfs3[i].ptr_storages = 1;
241      
242                 /* also enumerate the storages */
243                 dfs3[i].storages = TALLOC_ARRAY(ctx, DFS_STORAGE_INFO, j[i].referral_count);
244                 if (!dfs3[i].storages)
245                         return False;
246
247                 memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO));
248
249                 for(ii=0;ii<j[i].referral_count;ii++) {
250                         char* p; 
251                         pstring path;
252                         DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]);
253                         struct referral* ref = &(j[i].referral_list[ii]);
254           
255                         pstrcpy(path, ref->alternate_path);
256                         trim_char(path,'\\','\0');
257                         p = strrchr_m(path,'\\');
258                         if(p==NULL) {
259                                 DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
260                                 continue;
261                         }
262                         *p = '\0';
263                         DEBUG(5,("storage %d: %s.%s\n",ii,path,p+1));
264                         stor->state = 2; /* set all storages as ONLINE */
265                         init_unistr2(&stor->servername, path, UNI_STR_TERMINATE);
266                         init_unistr2(&stor->sharename,  p+1, UNI_STR_TERMINATE);
267                         stor->ptr_servername = stor->ptr_sharename = 1;
268                 }
269         }
270         return True;
271 }
272
273 static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level, 
274                                    DFS_INFO_CTR* ctr, struct junction_map* jn,
275                                    int num_jn)
276 {
277         /* do the levels */
278         switch(level) {
279         case 1:
280                 {
281                 DFS_INFO_1* dfs1;
282                 dfs1 = TALLOC_ARRAY(ctx, DFS_INFO_1, num_jn);
283                 if (!dfs1)
284                         return WERR_NOMEM;
285                 init_reply_dfs_info_1(jn, dfs1, num_jn);
286                 ctr->dfs.info1 = dfs1;
287                 break;
288                 }
289         case 2:
290                 {
291                 DFS_INFO_2* dfs2;
292                 dfs2 = TALLOC_ARRAY(ctx, DFS_INFO_2, num_jn);
293                 if (!dfs2)
294                         return WERR_NOMEM;
295                 init_reply_dfs_info_2(jn, dfs2, num_jn);
296                 ctr->dfs.info2 = dfs2;
297                 break;
298                 }
299         case 3:
300                 {
301                 DFS_INFO_3* dfs3;
302                 dfs3 = TALLOC_ARRAY(ctx, DFS_INFO_3, num_jn);
303                 if (!dfs3)
304                         return WERR_NOMEM;
305                 init_reply_dfs_info_3(ctx, jn, dfs3, num_jn);
306                 ctr->dfs.info3 = dfs3;
307                 break;
308                 }
309         default:
310                 return WERR_INVALID_PARAM;
311         }
312         return WERR_OK;
313 }
314       
315 WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u)
316 {
317         uint32 level = q_u->level;
318         struct junction_map jn[MAX_MSDFS_JUNCTIONS];
319         int num_jn = 0;
320
321         num_jn = enum_msdfs_links(p->mem_ctx, jn, ARRAY_SIZE(jn));
322         vfs_ChDir(p->conn,p->conn->connectpath);
323     
324         DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level));
325
326         r_u->ptr_buffer = level;
327         r_u->level = r_u->level2 = level;
328         r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1;
329         r_u->num_entries = r_u->num_entries2 = num_jn;
330         r_u->reshnd.ptr_hnd = 1;
331         r_u->reshnd.handle = num_jn;
332   
333         r_u->ctr = TALLOC_P(p->mem_ctx, DFS_INFO_CTR);
334         if (!r_u->ctr)
335                 return WERR_NOMEM;
336         ZERO_STRUCTP(r_u->ctr);
337         r_u->ctr->switch_value = level;
338         r_u->ctr->num_entries = num_jn;
339         r_u->ctr->ptr_dfs_ctr = 1;
340   
341         r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn);
342
343         return r_u->status;
344 }
345       
346 WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u, 
347                      DFS_R_DFS_GET_INFO *r_u)
348 {
349         UNISTR2* uni_path = &q_u->uni_path;
350         uint32 level = q_u->level;
351         int consumedcnt = sizeof(pstring);
352         pstring path;
353         struct junction_map jn;
354
355         unistr2_to_ascii(path, uni_path, sizeof(path)-1);
356         if(!create_junction(path, &jn))
357                 return WERR_DFS_NO_SUCH_SERVER;
358   
359         /* The following call can change the cwd. */
360         if(!get_referred_path(p->mem_ctx, path, &jn, &consumedcnt, NULL) || consumedcnt < strlen(path)) {
361                 vfs_ChDir(p->conn,p->conn->connectpath);
362                 return WERR_DFS_NO_SUCH_VOL;
363         }
364
365         vfs_ChDir(p->conn,p->conn->connectpath);
366         r_u->level = level;
367         r_u->ptr_ctr = 1;
368         r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1);
369   
370         return r_u->status;
371 }