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