xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_fhistory.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
40 /* Copyright (c) 2007, The Storage Networking Industry Association. */
41 
42 /*
43  * File history callback functions called by backup modules. NDMP file history
44  * supports 2 file history models: path based and inode/directory based.
45  * Backup/recover modules similar to unix dump/restore utilize the
46  * inode/directory based model. During the filesystem scan pass,
47  * ndmpd_file_history_dir() is called. During the file backup pass,
48  * ndmpd_file_history_node() is called. This model is appropriate for
49  * modules whose code is structured such that file name and file attribute
50  * data is not available at the same time. Backup/recover modules similar
51  * to tar or cpio utilize the path based model. The simple dump/restore module
52  * included with the SDK uses the path based model.
53  */
54 
55 #include <sys/stat.h>
56 #include <sys/types.h>
57 #include <dirent.h>
58 #include <errno.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include "ndmpd.h"
62 #include <dirent.h>
63 #include <bitmap.h>
64 
65 
66 #define	N_PATH_ENTRIES	1000
67 #define	N_FILE_ENTRIES	N_PATH_ENTRIES
68 #define	N_DIR_ENTRIES	1000
69 #define	N_NODE_ENTRIES	1000
70 
71 /* Figure an average of 32 bytes per path name */
72 #define	PATH_NAMEBUF_SIZE	(N_PATH_ENTRIES * 32)
73 
74 /* Figure an average of 16 bytes per file name */
75 #define	DIR_NAMEBUF_SIZE	(N_PATH_ENTRIES * 16)
76 
77 static boolean_t fh_requested(void *cookie);
78 static void ndmpd_file_history_cleanup_v2(ndmpd_session_t *session,
79     boolean_t send_flag);
80 static void ndmpd_file_history_cleanup_v3(ndmpd_session_t *session,
81     boolean_t send_flag);
82 static ndmpd_module_params_t *get_params(void *cookie);
83 
84 
85 /*
86  * Each file history as a separate message to the client.
87  */
88 static int ndmp_syncfh = 0;
89 
90 
91 /*
92  * ************************************************************************
93  * NDMP V2 HANDLERS
94  * ************************************************************************
95  */
96 
97 /*
98  * ndmpd_api_file_history_path_v2
99  *
100  * Add a file history path entry to the buffer.
101  * History data is buffered until the buffer is filled.
102  * Full buffers are then sent to the client.
103  *
104  * Parameters:
105  *   cookie   (input) - session pointer.
106  *   name     (input) - file name.
107  *		      NULL forces buffered data to be sent.
108  *   file_stat (input) - file status pointer.
109  *   fh_info  (input) - data stream position of file data used during
110  *		      fast restore.
111  *
112  * Returns:
113  *   0 - success
114  *  -1 - error
115  */
116 int
117 ndmpd_api_file_history_path_v2(void *cookie, char *name,
118     struct stat64 *file_stat, u_longlong_t fh_info)
119 {
120 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
121 	ndmp_fh_unix_path *entry;
122 
123 	if (name == NULL && session->ns_fh.fh_path_index == 0)
124 		return (0);
125 
126 	/*
127 	 * If the buffer does not have space
128 	 * for the current entry, send the buffered data to the client.
129 	 * A NULL name indicates that any buffered data should be sent.
130 	 */
131 	if (name == NULL ||
132 	    (ndmp_syncfh && session->ns_fh.fh_path_index != 0) ||
133 	    session->ns_fh.fh_path_index == N_PATH_ENTRIES ||
134 	    session->ns_fh.fh_path_name_buf_index + strlen(name) + 1 >
135 	    PATH_NAMEBUF_SIZE) {
136 		ndmp_fh_add_unix_path_request request;
137 
138 		NDMP_LOG(LOG_DEBUG,
139 		    "sending %ld entries", session->ns_fh.fh_path_index);
140 
141 		request.paths.paths_val = session->ns_fh.fh_path_entries;
142 		request.paths.paths_len = session->ns_fh.fh_path_index;
143 
144 		if (ndmp_send_request_lock(session->ns_connection,
145 		    NDMP_FH_ADD_UNIX_PATH, NDMP_NO_ERR, (void *) &request,
146 		    0) < 0) {
147 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
148 			return (-1);
149 		}
150 		session->ns_fh.fh_path_index = 0;
151 		session->ns_fh.fh_path_name_buf_index = 0;
152 	}
153 	if (name == NULL)
154 		return (0);
155 
156 	if (session->ns_fh.fh_path_entries == 0) {
157 		session->ns_fh.fh_path_entries = ndmp_malloc(N_PATH_ENTRIES *
158 		    sizeof (ndmp_fh_unix_path));
159 		if (session->ns_fh.fh_path_entries == 0)
160 			return (-1);
161 	}
162 	if (session->ns_fh.fh_path_name_buf == 0) {
163 		session->ns_fh.fh_path_name_buf =
164 		    ndmp_malloc(PATH_NAMEBUF_SIZE);
165 		if (session->ns_fh.fh_path_name_buf == 0)
166 			return (-1);
167 	}
168 	entry = &session->ns_fh.fh_path_entries[session->ns_fh.fh_path_index];
169 	ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype);
170 
171 	entry->name = &session->
172 	    ns_fh.fh_path_name_buf[session->ns_fh.fh_path_name_buf_index];
173 	(void) strlcpy(entry->name, name, PATH_NAMEBUF_SIZE);
174 	session->ns_fh.fh_path_name_buf_index += strlen(name) + 1;
175 	entry->fstat.mtime = (ulong_t)file_stat->st_mtime;
176 	entry->fstat.atime = (ulong_t)file_stat->st_atime;
177 	entry->fstat.ctime = (ulong_t)file_stat->st_ctime;
178 	entry->fstat.uid = file_stat->st_uid;
179 	entry->fstat.gid = file_stat->st_gid;
180 	entry->fstat.mode = (file_stat->st_mode) & 0x0fff;
181 	entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size);
182 	entry->fstat.fh_info = long_long_to_quad((u_longlong_t)fh_info);
183 	session->ns_fh.fh_path_index++;
184 	return (0);
185 }
186 
187 
188 /*
189  * ndmpd_api_file_history_dir_v2
190  *
191  * Add a file history dir entry to the buffer.
192  * History data is buffered until the buffer is filled.
193  * Full buffers are then sent to the client.
194  *
195  * Parameters:
196  *   cookie (input) - session pointer.
197  *   name   (input) - file name.
198  *		    NULL forces buffered data to be sent.
199  *   node   (input) - file inode.
200  *   parent (input) - file parent inode.
201  *		    Should equal node if the file is the root of
202  *		    the filesystem and has no parent.
203  *
204  * Returns:
205  *   0 - success
206  *  -1 - error
207  */
208 int
209 ndmpd_api_file_history_dir_v2(void *cookie, char *name, ulong_t node,
210     ulong_t parent)
211 {
212 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
213 	ndmp_fh_unix_dir *entry;
214 
215 	if (name == NULL && session->ns_fh.fh_dir_index == 0)
216 		return (0);
217 
218 	/*
219 	 * If the buffer does not have space for the current entry,
220 	 * send the buffered data to the client. A NULL name indicates
221 	 * that any buffered data should be sent.
222 	 */
223 	if (name == NULL ||
224 	    (ndmp_syncfh && session->ns_fh.fh_dir_index != 0) ||
225 	    session->ns_fh.fh_dir_index == N_DIR_ENTRIES ||
226 	    session->ns_fh.fh_dir_name_buf_index + strlen(name) + 1 >
227 	    DIR_NAMEBUF_SIZE) {
228 		ndmp_fh_add_unix_dir_request request;
229 
230 		NDMP_LOG(LOG_DEBUG,
231 		    "sending %ld entries", session->ns_fh.fh_dir_index);
232 
233 		request.dirs.dirs_val = session->ns_fh.fh_dir_entries;
234 		request.dirs.dirs_len = session->ns_fh.fh_dir_index;
235 		if (ndmp_send_request_lock(session->ns_connection,
236 		    NDMP_FH_ADD_UNIX_DIR, NDMP_NO_ERR, (void *) &request,
237 		    0) < 0) {
238 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
239 			return (-1);
240 		}
241 		session->ns_fh.fh_dir_index = 0;
242 		session->ns_fh.fh_dir_name_buf_index = 0;
243 	}
244 	if (name == NULL)
245 		return (0);
246 
247 	if (session->ns_fh.fh_dir_entries == 0) {
248 		session->ns_fh.fh_dir_entries = ndmp_malloc(N_DIR_ENTRIES
249 		    * sizeof (ndmp_fh_unix_dir));
250 		if (session->ns_fh.fh_dir_entries == 0)
251 			return (-1);
252 	}
253 	if (session->ns_fh.fh_dir_name_buf == 0) {
254 		session->ns_fh.fh_dir_name_buf = ndmp_malloc(DIR_NAMEBUF_SIZE);
255 		if (session->ns_fh.fh_dir_name_buf == 0)
256 			return (-1);
257 	}
258 	entry = &session->ns_fh.fh_dir_entries[session->ns_fh.fh_dir_index];
259 
260 	entry->name = &session->
261 	    ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index];
262 	(void) strlcpy(&session->
263 	    ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index],
264 	    name, PATH_NAMEBUF_SIZE);
265 	session->ns_fh.fh_dir_name_buf_index += strlen(name) + 1;
266 
267 	entry->node = node;
268 	entry->parent = parent;
269 
270 	session->ns_fh.fh_dir_index++;
271 	return (0);
272 }
273 
274 
275 /*
276  * ndmpd_api_file_history_node_v2
277  *
278  * Add a file history node entry to the buffer.
279  * History data is buffered until the buffer is filled.
280  * Full buffers are then sent to the client.
281  *
282  * Parameters:
283  *   cookie   (input) - session pointer.
284  *   node     (input) - file inode.
285  *	      must match a node from a prior ndmpd_api_file_history_dir()
286  *		      call.
287  *   file_stat (input) - file status pointer.
288  *		      0 forces buffered data to be sent.
289  *   fh_info  (input) - data stream position of file data used during
290  *		      fast restore.
291  *
292  * Returns:
293  *   0 - success
294  *  -1 - error.
295  */
296 int
297 ndmpd_api_file_history_node_v2(void *cookie, ulong_t node,
298     struct stat64 *file_stat, u_longlong_t fh_info)
299 {
300 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
301 	ndmp_fh_unix_node *entry;
302 
303 	if (file_stat == NULL && session->ns_fh.fh_node_index == 0)
304 		return (-1);
305 
306 	/*
307 	 * If the buffer does not have space
308 	 * for the current entry, send the buffered data to the client.
309 	 * A 0 file_stat pointer indicates that any buffered data should
310 	 * be sent.
311 	 */
312 	if (file_stat == NULL ||
313 	    (ndmp_syncfh && session->ns_fh.fh_node_index != 0) ||
314 	    session->ns_fh.fh_node_index == N_NODE_ENTRIES) {
315 		ndmp_fh_add_unix_node_request request;
316 
317 		NDMP_LOG(LOG_DEBUG,
318 		    "sending %ld entries", session->ns_fh.fh_node_index);
319 
320 		request.nodes.nodes_val = session->ns_fh.fh_node_entries;
321 		request.nodes.nodes_len = session->ns_fh.fh_node_index;
322 		/*
323 		 * Need to send Dir entry as well. Since Dir entry is more than
324 		 * Node entry, we may send a Node entry that hasn't have
325 		 * its dir entry sent. Therefore, we need to flush Dir entry
326 		 * as well everytime the Dir entry is send.
327 		 */
328 		(void) ndmpd_api_file_history_dir_v2(session, 0, 0, 0);
329 
330 		if (ndmp_send_request_lock(session->ns_connection,
331 		    NDMP_FH_ADD_UNIX_NODE, NDMP_NO_ERR, (void *) &request,
332 		    0) < 0) {
333 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
334 			return (-1);
335 		}
336 		session->ns_fh.fh_node_index = 0;
337 	}
338 	if (file_stat == NULL)
339 		return (0);
340 
341 	if (session->ns_fh.fh_node_entries == 0) {
342 		session->ns_fh.fh_node_entries = ndmp_malloc(N_NODE_ENTRIES
343 		    * sizeof (ndmp_fh_unix_node));
344 		if (session->ns_fh.fh_node_entries == 0)
345 			return (-1);
346 	}
347 	entry = &session->ns_fh.fh_node_entries[session->ns_fh.fh_node_index];
348 	ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype);
349 
350 	entry->node = node;
351 	entry->fstat.mtime = (ulong_t)file_stat->st_mtime;
352 	entry->fstat.atime = (ulong_t)file_stat->st_atime;
353 	entry->fstat.ctime = (ulong_t)file_stat->st_ctime;
354 	entry->fstat.uid = file_stat->st_uid;
355 	entry->fstat.gid = file_stat->st_gid;
356 	entry->fstat.mode = (file_stat->st_mode) & 0x0fff;
357 	entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size);
358 	entry->fstat.fh_info = long_long_to_quad(fh_info);
359 
360 	session->ns_fh.fh_node_index++;
361 	return (0);
362 }
363 
364 
365 /*
366  * ************************************************************************
367  * NDMP V3 HANDLERS
368  * ************************************************************************
369  */
370 
371 /*
372  * ndmpd_api_file_history_file_v3
373  *
374  * Add a file history file entry to the buffer.
375  * History data is buffered until the buffer is filled.
376  * Full buffers are then sent to the client.
377  *
378  * Parameters:
379  *   cookie   (input) - session pointer.
380  *   name     (input) - file name.
381  *		      NULL forces buffered data to be sent.
382  *   file_stat (input) - file status pointer.
383  *   fh_info  (input) - data stream position of file data used during
384  *		      fast restore.
385  *
386  * Returns:
387  *   0 - success
388  *  -1 - error
389  */
390 int
391 ndmpd_api_file_history_file_v3(void *cookie, char *name,
392     struct stat64 *file_stat, u_longlong_t fh_info)
393 {
394 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
395 	ndmp_file_v3 *file_entry;
396 	ndmp_file_name_v3 *file_name_entry;
397 	ndmp_file_stat_v3 *file_stat_entry;
398 	ndmp_fh_add_file_request_v3 request;
399 
400 	if (name == NULL && session->ns_fh_v3.fh_file_index == 0)
401 		return (0);
402 
403 	/*
404 	 * If the buffer does not have space
405 	 * for the current entry, send the buffered data to the client.
406 	 * A NULL name indicates that any buffered data should be sent.
407 	 */
408 	if (name == NULL ||
409 	    session->ns_fh_v3.fh_file_index == N_FILE_ENTRIES ||
410 	    session->ns_fh_v3.fh_file_name_buf_index + strlen(name) + 1 >
411 	    PATH_NAMEBUF_SIZE) {
412 
413 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
414 		    session->ns_fh_v3.fh_file_index);
415 
416 		request.files.files_len = session->ns_fh_v3.fh_file_index;
417 		request.files.files_val = session->ns_fh_v3.fh_files;
418 
419 		if (ndmp_send_request_lock(session->ns_connection,
420 		    NDMP_FH_ADD_FILE, NDMP_NO_ERR, (void *) &request, 0) < 0) {
421 			NDMP_LOG(LOG_DEBUG,
422 			    "Sending ndmp_fh_add_file request");
423 			return (-1);
424 		}
425 
426 		session->ns_fh_v3.fh_file_index = 0;
427 		session->ns_fh_v3.fh_file_name_buf_index = 0;
428 	}
429 
430 	if (name == NULL)
431 		return (0);
432 
433 	if (session->ns_fh_v3.fh_files == 0) {
434 		session->ns_fh_v3.fh_files = ndmp_malloc(sizeof (ndmp_file_v3) *
435 		    N_FILE_ENTRIES);
436 		if (session->ns_fh_v3.fh_files == 0)
437 			return (-1);
438 	}
439 
440 	if (session->ns_fh_v3.fh_file_names == 0) {
441 		session->ns_fh_v3.fh_file_names =
442 		    ndmp_malloc(sizeof (ndmp_file_name_v3) * N_FILE_ENTRIES);
443 		if (session->ns_fh_v3.fh_file_names == 0)
444 			return (-1);
445 	}
446 
447 	if (session->ns_fh_v3.fh_file_name_buf == 0) {
448 		session->ns_fh_v3.fh_file_name_buf =
449 		    ndmp_malloc(sizeof (char) * PATH_NAMEBUF_SIZE);
450 		if (session->ns_fh_v3.fh_file_name_buf == 0)
451 			return (-1);
452 	}
453 
454 	if (session->ns_fh_v3.fh_file_stats == 0) {
455 		session->ns_fh_v3.fh_file_stats =
456 		    ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_FILE_ENTRIES);
457 		if (session->ns_fh_v3.fh_file_stats == 0)
458 			return (-1);
459 	}
460 
461 	file_entry =
462 	    &session->ns_fh_v3.fh_files[session->ns_fh_v3.fh_file_index];
463 	file_name_entry =
464 	    &session->ns_fh_v3.fh_file_names[session->ns_fh_v3.fh_file_index];
465 	file_stat_entry =
466 	    &session->ns_fh_v3.fh_file_stats[session->ns_fh_v3.fh_file_index];
467 	file_entry->names.names_len = 1;
468 	file_entry->names.names_val = file_name_entry;
469 	file_entry->stats.stats_len = 1;
470 	file_entry->stats.stats_val = file_stat_entry;
471 	file_entry->node = long_long_to_quad(file_stat->st_ino);
472 	file_entry->fh_info = long_long_to_quad(fh_info);
473 
474 	file_name_entry->fs_type = NDMP_FS_UNIX;
475 	file_name_entry->ndmp_file_name_v3_u.unix_name =
476 	    &session->ns_fh_v3.fh_file_name_buf[session->
477 	    ns_fh_v3.fh_file_name_buf_index];
478 	(void) strlcpy(&session->ns_fh_v3.fh_file_name_buf[session->
479 	    ns_fh_v3.fh_file_name_buf_index], name, PATH_NAMEBUF_SIZE);
480 	session->ns_fh_v3.fh_file_name_buf_index += strlen(name) + 1;
481 	ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype);
482 
483 	file_stat_entry->invalid = 0;
484 	file_stat_entry->fs_type = NDMP_FS_UNIX;
485 	file_stat_entry->mtime = file_stat->st_mtime;
486 	file_stat_entry->atime = file_stat->st_atime;
487 	file_stat_entry->ctime = file_stat->st_ctime;
488 	file_stat_entry->owner = file_stat->st_uid;
489 	file_stat_entry->group = file_stat->st_gid;
490 	file_stat_entry->fattr = file_stat->st_mode & 0x0fff;
491 	file_stat_entry->size =
492 	    long_long_to_quad((u_longlong_t)file_stat->st_size);
493 	file_stat_entry->links = file_stat->st_nlink;
494 
495 	session->ns_fh_v3.fh_file_index++;
496 
497 	return (0);
498 }
499 
500 
501 /*
502  * ndmpd_api_file_history_dir_v3
503  *
504  * Add a file history dir entry to the buffer.
505  * History data is buffered until the buffer is filled.
506  * Full buffers are then sent to the client.
507  *
508  * Parameters:
509  *   cookie (input) - session pointer.
510  *   name   (input) - file name.
511  *		    NULL forces buffered data to be sent.
512  *   node   (input) - file inode.
513  *   parent (input) - file parent inode.
514  *		    Should equal node if the file is the root of
515  *		    the filesystem and has no parent.
516  *
517  * Returns:
518  *   0 - success
519  *  -1 - error
520  */
521 int
522 ndmpd_api_file_history_dir_v3(void *cookie, char *name, ulong_t node,
523     ulong_t parent)
524 {
525 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
526 	ndmp_dir_v3 *dir_entry;
527 	ndmp_file_name_v3 *dir_name_entry;
528 	ndmp_fh_add_dir_request_v3 request;
529 
530 	if (name == NULL && session->ns_fh_v3.fh_dir_index == 0)
531 		return (0);
532 
533 	/*
534 	 * If the buffer does not have space
535 	 * for the current entry, send the buffered data to the client.
536 	 * A NULL name indicates that any buffered data should be sent.
537 	 */
538 	if (name == NULL ||
539 	    session->ns_fh_v3.fh_dir_index == N_DIR_ENTRIES ||
540 	    session->ns_fh_v3.fh_dir_name_buf_index + strlen(name) + 1 >
541 	    DIR_NAMEBUF_SIZE) {
542 
543 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
544 		    session->ns_fh_v3.fh_dir_index);
545 
546 		request.dirs.dirs_val = session->ns_fh_v3.fh_dirs;
547 		request.dirs.dirs_len = session->ns_fh_v3.fh_dir_index;
548 
549 		if (ndmp_send_request_lock(session->ns_connection,
550 		    NDMP_FH_ADD_DIR, NDMP_NO_ERR, (void *) &request, 0) < 0) {
551 			NDMP_LOG(LOG_DEBUG,
552 			    "Sending ndmp_fh_add_dir request");
553 			return (-1);
554 		}
555 
556 		session->ns_fh_v3.fh_dir_index = 0;
557 		session->ns_fh_v3.fh_dir_name_buf_index = 0;
558 	}
559 
560 	if (name == NULL)
561 		return (0);
562 
563 	if (session->ns_fh_v3.fh_dirs == 0) {
564 		session->ns_fh_v3.fh_dirs =
565 		    ndmp_malloc(sizeof (ndmp_dir_v3) * N_DIR_ENTRIES);
566 		if (session->ns_fh_v3.fh_dirs == 0)
567 			return (-1);
568 	}
569 
570 	if (session->ns_fh_v3.fh_dir_names == 0) {
571 		session->ns_fh_v3.fh_dir_names =
572 		    ndmp_malloc(sizeof (ndmp_file_name_v3) * N_DIR_ENTRIES);
573 		if (session->ns_fh_v3.fh_dir_names == 0)
574 			return (-1);
575 	}
576 
577 	if (session->ns_fh_v3.fh_dir_name_buf == 0) {
578 		session->ns_fh_v3.fh_dir_name_buf =
579 		    ndmp_malloc(sizeof (char) * DIR_NAMEBUF_SIZE);
580 		if (session->ns_fh_v3.fh_dir_name_buf == 0)
581 			return (-1);
582 	}
583 
584 	dir_entry = &session->ns_fh_v3.fh_dirs[session->ns_fh_v3.fh_dir_index];
585 	dir_name_entry =
586 	    &session->ns_fh_v3.fh_dir_names[session->ns_fh_v3.fh_dir_index];
587 
588 	dir_name_entry->fs_type = NDMP_FS_UNIX;
589 	dir_name_entry->ndmp_file_name_v3_u.unix_name =
590 	    &session->ns_fh_v3.fh_dir_name_buf[session->
591 	    ns_fh_v3.fh_dir_name_buf_index];
592 
593 	(void) strlcpy(&session->ns_fh_v3.fh_dir_name_buf[session->
594 	    ns_fh_v3.fh_dir_name_buf_index], name, PATH_NAMEBUF_SIZE);
595 	session->ns_fh_v3.fh_dir_name_buf_index += strlen(name) + 1;
596 
597 	dir_entry->names.names_len = 1;
598 	dir_entry->names.names_val = dir_name_entry;
599 	dir_entry->node = long_long_to_quad(node);
600 	dir_entry->parent = long_long_to_quad(parent);
601 
602 	session->ns_fh_v3.fh_dir_index++;
603 
604 	return (0);
605 }
606 
607 
608 /*
609  * ndmpd_api_file_history_node_v3
610  *
611  * Add a file history node entry to the buffer.
612  * History data is buffered until the buffer is filled.
613  * Full buffers are then sent to the client.
614  *
615  * Parameters:
616  *   cookie   (input) - session pointer.
617  *   node     (input) - file inode.
618  *		must match a node from a prior ndmpd_api_file_history_dir()
619  *		      call.
620  *   file_stat (input) - file status pointer.
621  *		      0 forces buffered data to be sent.
622  *   fh_info  (input) - data stream position of file data used during
623  *		      fast restore.
624  *
625  * Returns:
626  *   0 - success
627  *  -1 - error.
628  */
629 int
630 ndmpd_api_file_history_node_v3(void *cookie, ulong_t node,
631     struct stat64 *file_stat, u_longlong_t fh_info)
632 {
633 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
634 	ndmp_node_v3 *node_entry;
635 	ndmp_file_stat_v3 *file_stat_entry;
636 	ndmp_fh_add_node_request_v3 request;
637 
638 	if (file_stat == NULL && session->ns_fh_v3.fh_node_index == 0)
639 		return (0);
640 
641 	/*
642 	 * If the buffer does not have space
643 	 * for the current entry, send the buffered data to the client.
644 	 * A 0 file_stat pointer indicates that any buffered data should
645 	 * be sent.
646 	 */
647 	if (file_stat == NULL ||
648 	    session->ns_fh_v3.fh_node_index == N_NODE_ENTRIES) {
649 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
650 		    session->ns_fh_v3.fh_node_index);
651 
652 		/*
653 		 * Need to send Dir entry as well. Since Dir entry is more
654 		 * than a Node entry, we may send a Node entry that hasn't
655 		 * had its Dir entry sent. Therefore, we need to flush Dir
656 		 * entry as well every time the Dir entry is sent.
657 		 */
658 		(void) ndmpd_api_file_history_dir_v3(session, 0, 0, 0);
659 
660 		request.nodes.nodes_len = session->ns_fh_v3.fh_node_index;
661 		request.nodes.nodes_val = session->ns_fh_v3.fh_nodes;
662 
663 		if (ndmp_send_request_lock(session->ns_connection,
664 		    NDMP_FH_ADD_NODE,
665 		    NDMP_NO_ERR, (void *) &request, 0) < 0) {
666 			NDMP_LOG(LOG_DEBUG,
667 			    "Sending ndmp_fh_add_node request");
668 			return (-1);
669 		}
670 
671 		session->ns_fh_v3.fh_node_index = 0;
672 	}
673 
674 	if (file_stat == NULL)
675 		return (0);
676 
677 	if (session->ns_fh_v3.fh_nodes == 0) {
678 		session->ns_fh_v3.fh_nodes =
679 		    ndmp_malloc(sizeof (ndmp_node_v3) * N_NODE_ENTRIES);
680 		if (session->ns_fh_v3.fh_nodes == 0)
681 			return (-1);
682 	}
683 
684 	if (session->ns_fh_v3.fh_node_stats == 0) {
685 		session->ns_fh_v3.fh_node_stats =
686 		    ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_NODE_ENTRIES);
687 		if (session->ns_fh_v3.fh_node_stats == 0)
688 			return (-1);
689 	}
690 
691 	node_entry =
692 	    &session->ns_fh_v3.fh_nodes[session->ns_fh_v3.fh_node_index];
693 
694 	file_stat_entry =
695 	    &session->ns_fh_v3.fh_node_stats[session->ns_fh_v3.fh_node_index];
696 	ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype);
697 
698 	file_stat_entry->invalid = 0;
699 	file_stat_entry->fs_type = NDMP_FS_UNIX;
700 	file_stat_entry->mtime = file_stat->st_mtime;
701 	file_stat_entry->atime = file_stat->st_atime;
702 	file_stat_entry->ctime = file_stat->st_ctime;
703 	file_stat_entry->owner = file_stat->st_uid;
704 	file_stat_entry->group = file_stat->st_gid;
705 	file_stat_entry->fattr = file_stat->st_mode & 0x0fff;
706 	file_stat_entry->size =
707 	    long_long_to_quad((u_longlong_t)file_stat->st_size);
708 	file_stat_entry->links = file_stat->st_nlink;
709 
710 	node_entry->stats.stats_len = 1;
711 	node_entry->stats.stats_val = file_stat_entry;
712 	node_entry->node = long_long_to_quad((u_longlong_t)node);
713 	node_entry->fh_info = long_long_to_quad(fh_info);
714 
715 	session->ns_fh_v3.fh_node_index++;
716 
717 	return (0);
718 }
719 
720 
721 /*
722  * ************************************************************************
723  * NDMP V4 HANDLERS
724  * ************************************************************************
725  */
726 
727 
728 /*
729  * ndmpd_fhpath_v3_cb
730  *
731  * Callback function for file history path information
732  */
733 int
734 ndmpd_fhpath_v3_cb(lbr_fhlog_call_backs_t *cbp, char *path, struct stat64 *stp,
735     u_longlong_t off)
736 {
737 	int err;
738 	ndmp_lbr_params_t *nlp;
739 	ndmpd_module_params_t *params;
740 
741 	if (!cbp) {
742 		err = -1;
743 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
744 	} else if (!cbp->fh_cookie) {
745 		err = -1;
746 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
747 	} else if (!path) {
748 		err = -1;
749 		NDMP_LOG(LOG_DEBUG, "path is NULL");
750 	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
751 		err = -1;
752 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
753 	} else
754 		err = 0;
755 
756 	if (err != 0)
757 		return (0);
758 
759 	NDMP_LOG(LOG_DEBUG, "pname(%s)", path);
760 
761 	err = 0;
762 	if (NLP_ISSET(nlp, NLPF_FH)) {
763 		if (!NLP_ISSET(nlp, NLPF_DIRECT)) {
764 			NDMP_LOG(LOG_DEBUG, "DAR NOT SET!");
765 			off = 0LL;
766 		}
767 
768 		params = get_params(cbp->fh_cookie);
769 		if (!params || !params->mp_file_history_path_func) {
770 			err = -1;
771 		} else {
772 			char *p = ndmp_get_relative_path(get_bk_path_v3(params),
773 			    path);
774 			if ((err = ndmpd_api_file_history_file_v3(cbp->
775 			    fh_cookie, p, stp, off)) < 0)
776 				NDMP_LOG(LOG_DEBUG, "\"%s\" %d", path, err);
777 		}
778 	}
779 
780 	return (err);
781 }
782 
783 
784 /*
785  * ndmpd_fhdir_v3_cb
786  *
787  * Callback function for file history dir information
788  */
789 int
790 ndmpd_fhdir_v3_cb(lbr_fhlog_call_backs_t *cbp, char *dir, struct stat64 *stp)
791 {
792 	char nm[PATH_MAX+1];
793 	int nml;
794 	int err;
795 	ulong_t ino, pino;
796 	ulong_t pos;
797 	ndmp_lbr_params_t *nlp;
798 	ndmpd_module_params_t *params;
799 	DIR *dirp;
800 	char dirpath[PATH_MAX];
801 
802 	if (!cbp) {
803 		err = -1;
804 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
805 	} else if (!cbp->fh_cookie) {
806 		err = -1;
807 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
808 	} else if (!dir) {
809 		err = -1;
810 		NDMP_LOG(LOG_DEBUG, "dir is NULL");
811 	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
812 		err = -1;
813 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
814 	} else
815 		err = 0;
816 
817 	if (err != 0)
818 		return (0);
819 
820 	NDMP_LOG(LOG_DEBUG, "d(%s)", dir);
821 
822 	if (!NLP_ISSET(nlp, NLPF_FH))
823 		return (0);
824 
825 	/*
826 	 * Veritas net_backup accepts only 2 as the inode number of the backup
827 	 * root directory.  The other way compares the path against the
828 	 * backup path which is slower.
829 	 */
830 	if (stp->st_ino == nlp->nlp_bkdirino)
831 		pino = ROOT_INODE;
832 	else
833 		pino = stp->st_ino;
834 
835 	/*
836 	 * There is nothing below this directory to be backed up.
837 	 * If there was, the bit for this directory would have
838 	 * been set.  Backup root directory is exception.  We
839 	 * always send the dir file history records of it.
840 	 */
841 	if (pino != ROOT_INODE &&
842 	    !dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino)) {
843 		NDMP_LOG(LOG_DEBUG, "nothing below here");
844 		return (0);
845 	}
846 
847 	params = nlp->nlp_params;
848 	if (!params || !params->mp_file_history_dir_func)
849 		return (-1);
850 
851 	pos = 0;
852 	err = 0;
853 
854 	dirp = opendir(dir);
855 	if (dirp == NULL)
856 		return (0);
857 
858 	do {
859 		nml = PATH_MAX;
860 		err = dp_readdir(dirp, &pos, nm, &nml, &ino);
861 		if (err != 0) {
862 			NDMP_LOG(LOG_DEBUG,
863 			    "%d reading pos %u dir \"%s\"", err, pos, dir);
864 			break;
865 		}
866 		if (nml == 0)
867 			break;
868 		nm[nml] = '\0';
869 
870 		if (pino == ROOT_INODE) {
871 			if (rootfs_dot_or_dotdot(nm))
872 				ino = ROOT_INODE;
873 		} else if (ino == nlp->nlp_bkdirino && IS_DOTDOT(nm)) {
874 			NDMP_LOG(LOG_DEBUG, "nm(%s): %lu", nm, ino);
875 			ino = ROOT_INODE;
876 		}
877 
878 		if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)ino))
879 			continue;
880 
881 		/*
882 		 * If the entry is on exclusion list dont send the info
883 		 */
884 		if (tlm_is_excluded(dir, nm, ndmp_excl_list)) {
885 			NDMP_LOG(LOG_DEBUG,
886 			    "name \"%s\" skipped", nm == 0 ? "nil" : nm);
887 			continue;
888 		}
889 
890 		err = (*params->mp_file_history_dir_func)(cbp->fh_cookie, nm,
891 		    ino, pino);
892 		if (err < 0) {
893 			NDMP_LOG(LOG_DEBUG, "\"%s\": %d", dir, err);
894 			break;
895 		}
896 
897 		/*
898 		 * This is a requirement by some DMA's (net_vault) that during
899 		 * the incremental backup, the node info should also be sent
900 		 * along with the dir info for all directories leading to a
901 		 * backed up file.
902 		 */
903 		if (ndmp_fhinode) {
904 			struct stat64 ret_attr;
905 
906 			(void) strlcpy(dirpath, dir, PATH_MAX);
907 			(void) strlcat(dirpath, "/", PATH_MAX);
908 			(void) strlcat(dirpath, nm, PATH_MAX);
909 			err = stat64(dirpath, &ret_attr);
910 			if (err != 0) {
911 				NDMP_LOG(LOG_DEBUG,
912 				    "Error looking up %s", nm);
913 				break;
914 			}
915 
916 			if (S_ISDIR(ret_attr.st_mode)) {
917 				err = (*params->mp_file_history_node_func)(cbp->
918 				    fh_cookie, ino, &ret_attr, 0);
919 				if (err < 0) {
920 					NDMP_LOG(LOG_DEBUG, "\"%s/\": %d",
921 					    dir, err);
922 					break;
923 				}
924 			}
925 		}
926 	} while (err == 0);
927 
928 	(void) closedir(dirp);
929 	return (err);
930 }
931 
932 
933 /*
934  * ndmpd_fhnode_v3_cb
935  *
936  * Callback function for file history node information
937  */
938 int
939 ndmpd_fhnode_v3_cb(lbr_fhlog_call_backs_t *cbp, char *dir, char *file,
940     struct stat64 *stp, u_longlong_t off)
941 {
942 	int err;
943 	ulong_t ino;
944 	ndmp_lbr_params_t *nlp;
945 	ndmpd_module_params_t *params;
946 
947 	if (!cbp) {
948 		err = -1;
949 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
950 	} else if (!cbp->fh_cookie) {
951 		err = -1;
952 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
953 	} else if (!dir) {
954 		err = -1;
955 		NDMP_LOG(LOG_DEBUG, "dir is NULL");
956 	} else if (!file) {
957 		err = -1;
958 		NDMP_LOG(LOG_DEBUG, "file is NULL");
959 	} else if (!stp) {
960 		err = -1;
961 		NDMP_LOG(LOG_DEBUG, "stp is NULL");
962 	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
963 		err = -1;
964 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
965 	} else {
966 		err = 0;
967 	}
968 
969 	if (err != 0)
970 		return (0);
971 
972 	NDMP_LOG(LOG_DEBUG, "d(%s), f(%s)", dir, file);
973 
974 	err = 0;
975 	if (NLP_ISSET(nlp, NLPF_FH)) {
976 		if (!NLP_ISSET(nlp, NLPF_DIRECT))
977 			off = 0LL;
978 		if (stp->st_ino == nlp->nlp_bkdirino) {
979 			ino = ROOT_INODE;
980 			NDMP_LOG(LOG_DEBUG,
981 			    "bkroot %d -> %d", stp->st_ino, ROOT_INODE);
982 		} else
983 			ino = stp->st_ino;
984 
985 		params = nlp->nlp_params;
986 		if (!params || !params->mp_file_history_node_func)
987 			err = -1;
988 		else if ((err = (*params->mp_file_history_node_func)(cbp->
989 		    fh_cookie, ino, stp, off)) < 0)
990 			NDMP_LOG(LOG_DEBUG, "\"%s/%s\" %d", dir, file, err);
991 	}
992 
993 	return (err);
994 }
995 
996 
997 /*
998  * ndmp_send_recovery_stat_v3
999  *
1000  * Send the recovery status to the DMA
1001  */
1002 int
1003 ndmp_send_recovery_stat_v3(ndmpd_module_params_t *params,
1004     ndmp_lbr_params_t *nlp, int idx, int stat)
1005 {
1006 	int rv;
1007 	mem_ndmp_name_v3_t *ep;
1008 
1009 	rv = -1;
1010 	if (!params) {
1011 		NDMP_LOG(LOG_DEBUG, "params == NULL");
1012 	} else if (!params->mp_file_recovered_func) {
1013 		NDMP_LOG(LOG_DEBUG, "paramsfile_recovered_func == NULL");
1014 	} else if (!nlp) {
1015 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1016 	} else if (idx < 0) {
1017 		NDMP_LOG(LOG_DEBUG, "idx(%d) < 0", idx);
1018 	} else if (!(ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, idx))) {
1019 		NDMP_LOG(LOG_DEBUG, "nlist[%d] == NULL", idx);
1020 	} else if (!ep->nm3_opath) {
1021 		NDMP_LOG(LOG_DEBUG, "nlist[%d].nm3_opath == NULL", idx);
1022 	} else {
1023 		NDMP_LOG(LOG_DEBUG,
1024 		    "ep[%d].nm3_opath \"%s\"", idx, ep->nm3_opath);
1025 		rv = MOD_FILERECOVERD(params, ep->nm3_opath, stat);
1026 	}
1027 
1028 	return (rv);
1029 }
1030 
1031 
1032 /*
1033  * ndmpd_path_restored_v3
1034  *
1035  * Send the recovery status and the information for the restored
1036  * path.
1037  */
1038 /*ARGSUSED*/
1039 int
1040 ndmpd_path_restored_v3(lbr_fhlog_call_backs_t *cbp, char *name,
1041     struct stat64 *st, u_longlong_t ll_idx)
1042 {
1043 	int rv;
1044 	ndmp_lbr_params_t *nlp;
1045 	ndmpd_module_params_t *params;
1046 	int idx = (int)ll_idx;
1047 
1048 	if (!cbp) {
1049 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
1050 		return (-1);
1051 	}
1052 	if (!name) {
1053 		NDMP_LOG(LOG_DEBUG, "name is NULL");
1054 		return (-1);
1055 	}
1056 
1057 	NDMP_LOG(LOG_DEBUG, "name: \"%s\", idx: %d", name, idx);
1058 
1059 	nlp = ndmp_get_nlp(cbp->fh_cookie);
1060 	if (!nlp) {
1061 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
1062 		return (-1);
1063 	}
1064 	if (idx < 0 || idx >= nlp->nlp_nfiles) {
1065 		NDMP_LOG(LOG_DEBUG, "Invalid idx: %d", idx);
1066 		return (-1);
1067 	}
1068 	params = nlp->nlp_params;
1069 	if (!params || !params->mp_file_recovered_func)
1070 		return (-1);
1071 
1072 	if (nlp->nlp_lastidx == -1)
1073 		nlp->nlp_lastidx = idx;
1074 
1075 	rv = 0;
1076 	(void) bm_setone(nlp->nlp_rsbm, (u_longlong_t)idx);
1077 	/*
1078 	 * Note: We should set the nm3_err here.
1079 	 */
1080 	if (nlp->nlp_lastidx != idx) {
1081 		rv = ndmp_send_recovery_stat_v3(params, nlp, nlp->nlp_lastidx,
1082 		    0);
1083 		nlp->nlp_lastidx = idx;
1084 	}
1085 
1086 	return (rv);
1087 }
1088 
1089 
1090 
1091 /*
1092  * ndmpd_file_history_init
1093  *
1094  * Initialize file history variables.
1095  * Note that the entry and name buffers are not allocated here.
1096  * Since it is not know if the backup module will be sending file history
1097  * data or what kind of data (path or dir/node), the entry and name
1098  * buffers are not allocated until the first call to one of the file history
1099  * entry functions is made. This way resources are only allocated as
1100  * needed.
1101  *
1102  * Parameters:
1103  *   session (input) - session pointer.
1104  *
1105  * Returns:
1106  *   void
1107  */
1108 void
1109 ndmpd_file_history_init(ndmpd_session_t *session)
1110 {
1111 	session->ns_fh.fh_path_entries = 0;
1112 	session->ns_fh.fh_dir_entries = 0;
1113 	session->ns_fh.fh_node_entries = 0;
1114 	session->ns_fh.fh_path_name_buf = 0;
1115 	session->ns_fh.fh_dir_name_buf = 0;
1116 	session->ns_fh.fh_path_index = 0;
1117 	session->ns_fh.fh_dir_index = 0;
1118 	session->ns_fh.fh_node_index = 0;
1119 	session->ns_fh.fh_path_name_buf_index = 0;
1120 	session->ns_fh.fh_dir_name_buf_index = 0;
1121 
1122 	/*
1123 	 * V3.
1124 	 */
1125 	session->ns_fh_v3.fh_files = 0;
1126 	session->ns_fh_v3.fh_dirs = 0;
1127 	session->ns_fh_v3.fh_nodes = 0;
1128 	session->ns_fh_v3.fh_file_names = 0;
1129 	session->ns_fh_v3.fh_dir_names = 0;
1130 	session->ns_fh_v3.fh_file_stats = 0;
1131 	session->ns_fh_v3.fh_node_stats = 0;
1132 	session->ns_fh_v3.fh_file_name_buf = 0;
1133 	session->ns_fh_v3.fh_dir_name_buf = 0;
1134 	session->ns_fh_v3.fh_file_index = 0;
1135 	session->ns_fh_v3.fh_dir_index = 0;
1136 	session->ns_fh_v3.fh_node_index = 0;
1137 	session->ns_fh_v3.fh_file_name_buf_index = 0;
1138 	session->ns_fh_v3.fh_dir_name_buf_index = 0;
1139 }
1140 
1141 
1142 /*
1143  * ndmpd_file_history_cleanup_v2
1144  *
1145  * Send (or discard) any buffered file history entries.
1146  *
1147  * Parameters:
1148  *   session  (input) - session pointer.
1149  *   send_flag (input) - if TRUE  buffered entries are sent.
1150  *		      if FALSE buffered entries are discarded.
1151  *
1152  * Returns:
1153  *   void
1154  */
1155 static void
1156 ndmpd_file_history_cleanup_v2(ndmpd_session_t *session, boolean_t send_flag)
1157 {
1158 	if (send_flag == TRUE) {
1159 		(void) ndmpd_api_file_history_path_v2(session, 0, 0, 0);
1160 		(void) ndmpd_api_file_history_dir_v2(session, 0, 0, 0);
1161 		(void) ndmpd_api_file_history_node_v2(session, 0, 0, 0);
1162 	}
1163 
1164 	if (session->ns_fh.fh_path_entries != 0) {
1165 		free(session->ns_fh.fh_path_entries);
1166 		session->ns_fh.fh_path_entries = 0;
1167 	}
1168 	if (session->ns_fh.fh_dir_entries != 0) {
1169 		free(session->ns_fh.fh_dir_entries);
1170 		session->ns_fh.fh_dir_entries = 0;
1171 	}
1172 	if (session->ns_fh.fh_node_entries != 0) {
1173 		free(session->ns_fh.fh_node_entries);
1174 		session->ns_fh.fh_node_entries = 0;
1175 	}
1176 	if (session->ns_fh.fh_path_name_buf != 0) {
1177 		free(session->ns_fh.fh_path_name_buf);
1178 		session->ns_fh.fh_path_name_buf = 0;
1179 	}
1180 	if (session->ns_fh.fh_dir_name_buf != 0) {
1181 		free(session->ns_fh.fh_dir_name_buf);
1182 		session->ns_fh.fh_dir_name_buf = 0;
1183 	}
1184 	session->ns_fh.fh_path_index = 0;
1185 	session->ns_fh.fh_dir_index = 0;
1186 	session->ns_fh.fh_node_index = 0;
1187 	session->ns_fh.fh_path_name_buf_index = 0;
1188 	session->ns_fh.fh_dir_name_buf_index = 0;
1189 }
1190 
1191 
1192 /*
1193  * ndmpd_file_history_cleanup_v3
1194  *
1195  * Send (or discard) any buffered file history entries.
1196  *
1197  * Parameters:
1198  *   session  (input) - session pointer.
1199  *   send_flag (input) - if TRUE  buffered entries are sent.
1200  *		      if FALSE buffered entries are discarded.
1201  *
1202  * Returns:
1203  *   void
1204  */
1205 static void
1206 ndmpd_file_history_cleanup_v3(ndmpd_session_t *session, boolean_t send_flag)
1207 {
1208 	if (send_flag == TRUE) {
1209 		(void) ndmpd_api_file_history_file_v3(session, 0, 0, 0);
1210 		(void) ndmpd_api_file_history_dir_v3(session, 0, 0, 0);
1211 		(void) ndmpd_api_file_history_node_v3(session, 0, 0, 0);
1212 	}
1213 
1214 	if (session->ns_fh_v3.fh_files != 0) {
1215 		free(session->ns_fh_v3.fh_files);
1216 		session->ns_fh_v3.fh_files = 0;
1217 	}
1218 	if (session->ns_fh_v3.fh_dirs != 0) {
1219 		free(session->ns_fh_v3.fh_dirs);
1220 		session->ns_fh_v3.fh_dirs = 0;
1221 	}
1222 	if (session->ns_fh_v3.fh_nodes != 0) {
1223 		free(session->ns_fh_v3.fh_nodes);
1224 		session->ns_fh_v3.fh_nodes = 0;
1225 	}
1226 	if (session->ns_fh_v3.fh_file_names != 0) {
1227 		free(session->ns_fh_v3.fh_file_names);
1228 		session->ns_fh_v3.fh_file_names = 0;
1229 	}
1230 	if (session->ns_fh_v3.fh_dir_names != 0) {
1231 		free(session->ns_fh_v3.fh_dir_names);
1232 		session->ns_fh_v3.fh_dir_names = 0;
1233 	}
1234 	if (session->ns_fh_v3.fh_file_stats != 0) {
1235 		free(session->ns_fh_v3.fh_file_stats);
1236 		session->ns_fh_v3.fh_file_stats = 0;
1237 	}
1238 	if (session->ns_fh_v3.fh_node_stats != 0) {
1239 		free(session->ns_fh_v3.fh_node_stats);
1240 		session->ns_fh_v3.fh_node_stats = 0;
1241 	}
1242 	if (session->ns_fh_v3.fh_file_name_buf != 0) {
1243 		free(session->ns_fh_v3.fh_file_name_buf);
1244 		session->ns_fh_v3.fh_file_name_buf = 0;
1245 	}
1246 	if (session->ns_fh_v3.fh_dir_name_buf != 0) {
1247 		free(session->ns_fh_v3.fh_dir_name_buf);
1248 		session->ns_fh_v3.fh_dir_name_buf = 0;
1249 	}
1250 
1251 	session->ns_fh_v3.fh_file_index = 0;
1252 	session->ns_fh_v3.fh_dir_index = 0;
1253 	session->ns_fh_v3.fh_node_index = 0;
1254 	session->ns_fh_v3.fh_file_name_buf_index = 0;
1255 	session->ns_fh_v3.fh_dir_name_buf_index = 0;
1256 }
1257 
1258 
1259 /*
1260  * ndmpd_file_history_cleanup
1261  *
1262  * Send any pending posts and clean up
1263  */
1264 void
1265 ndmpd_file_history_cleanup(ndmpd_session_t *session, boolean_t send_flag)
1266 {
1267 	switch (session->ns_protocol_version) {
1268 	case 1:
1269 	case 2:
1270 		ndmpd_file_history_cleanup_v2(session, send_flag);
1271 		break;
1272 	case 3:
1273 	case 4:
1274 		ndmpd_file_history_cleanup_v3(session, send_flag);
1275 		break;
1276 	default:
1277 		NDMP_LOG(LOG_DEBUG, "Unknown version %d",
1278 		    session->ns_protocol_version);
1279 	}
1280 }
1281 
1282 /*
1283  * get_params
1284  *
1285  * Callbacks from LBR.
1286  */
1287 static ndmpd_module_params_t *
1288 get_params(void *cookie)
1289 {
1290 	ndmp_lbr_params_t *nlp;
1291 
1292 	if ((nlp = ndmp_get_nlp(cookie)) == NULL)
1293 		return (NULL);
1294 
1295 	return (nlp->nlp_params);
1296 }
1297 
1298 
1299 /*
1300  * fh_requested
1301  *
1302  * Check in LB parameters if file history is requested
1303  */
1304 static boolean_t
1305 fh_requested(void *cookie)
1306 {
1307 	ndmp_lbr_params_t *nlp;
1308 
1309 	if ((nlp = ndmp_get_nlp(cookie)) == NULL) {
1310 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
1311 		return (FALSE);
1312 	}
1313 
1314 	NDMP_LOG(LOG_DEBUG, "nlp_fh %c", NDMP_YORN(NLP_ISSET(nlp, NLPF_FH)));
1315 
1316 	return (NLP_ISSET(nlp, NLPF_FH));
1317 }
1318 
1319 
1320 /*
1321  * ndmpd_file_history_path
1322  *
1323  * Generates file history path information posts
1324  *
1325  * Note:
1326  *   Action must be determined when the 'dir' and/or 'file'
1327  *   arguments of ndmpd_file_history_path(), ndmpd_file_history_dir(), and
1328  *   ndmpd_file_history_node() are NULL.
1329  */
1330 /*ARGSUSED*/
1331 int
1332 ndmpd_file_history_path(lbr_fhlog_call_backs_t *cbp, char *path,
1333     struct stat64 *stp, u_longlong_t off)
1334 {
1335 	int err;
1336 	ndmpd_module_params_t *params;
1337 
1338 	if (!cbp) {
1339 		err = -1;
1340 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
1341 	} else if (!cbp->fh_cookie) {
1342 		err = -1;
1343 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
1344 	} else if (!path) {
1345 		err = -1;
1346 		NDMP_LOG(LOG_DEBUG, "path is NULL");
1347 	} else if (!stp) {
1348 		err = -1;
1349 		NDMP_LOG(LOG_DEBUG, "stp is NULL");
1350 	} else
1351 		err = 0;
1352 
1353 	if (err != 0)
1354 		return (0);
1355 
1356 	NDMP_LOG(LOG_DEBUG, "path: \"%s\"", path);
1357 
1358 	err = 0;
1359 	if (fh_requested(cbp->fh_cookie)) {
1360 		params = get_params(cbp->fh_cookie);
1361 		if (params == NULL || params->mp_file_history_path_func == NULL)
1362 			err = -1;
1363 		else if ((err = (*params->mp_file_history_path_func)(cbp->
1364 		    fh_cookie, path, stp, 0)) < 0)
1365 			NDMP_LOG(LOG_DEBUG, "\"%s\": %d", path, err);
1366 	}
1367 
1368 	return (err);
1369 }
1370 
1371 
1372 /*
1373  * ndmpd_file_history_dir
1374  *
1375  * Generate file history directory information posts
1376  */
1377 int
1378 ndmpd_file_history_dir(lbr_fhlog_call_backs_t *cbp, char *dir,
1379     struct stat64 *stp)
1380 {
1381 	char nm[PATH_MAX+1];
1382 	int nml;
1383 	int err;
1384 	ulong_t ino, pino;
1385 	ulong_t pos;
1386 	ndmp_lbr_params_t *nlp;
1387 	ndmpd_module_params_t *params;
1388 	DIR *dirp;
1389 	char dirpath[PATH_MAX];
1390 
1391 	if (!cbp) {
1392 		err = -1;
1393 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
1394 	} else if (!cbp->fh_cookie) {
1395 		err = -1;
1396 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
1397 	} else if (!dir) {
1398 		err = -1;
1399 		NDMP_LOG(LOG_DEBUG, "dir is NULL");
1400 	} else if (!stp) {
1401 		err = -1;
1402 		NDMP_LOG(LOG_DEBUG, "stp is NULL");
1403 	} if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
1404 		err = -1;
1405 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
1406 	} else
1407 		err = 0;
1408 
1409 	if (err != 0)
1410 		return (0);
1411 
1412 	NDMP_LOG(LOG_DEBUG, "dir: \"%s\"", dir);
1413 
1414 	if (!fh_requested(cbp->fh_cookie))
1415 		return (0);
1416 
1417 	/*
1418 	 * Veritas net_backup accepts only 2 as the inode number of the backup
1419 	 * root directory.  The other way compares the path against the
1420 	 * backup path which is slower.
1421 	 */
1422 	if (stp->st_ino == nlp->nlp_bkdirino)
1423 		pino = ROOT_INODE;
1424 	else
1425 		pino = stp->st_ino;
1426 
1427 	/*
1428 	 * There is nothing below this directory to be backed up.
1429 	 * If there was, the bit for this directory would have
1430 	 * been set.  Backup root directory is exception.  We
1431 	 * always send the dir file history records of it.
1432 	 */
1433 	if (pino != ROOT_INODE &&
1434 	    !dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino)) {
1435 		NDMP_LOG(LOG_DEBUG, "nothing below here");
1436 		return (0);
1437 	}
1438 
1439 	params = get_params(cbp->fh_cookie);
1440 	if (params == NULL || params->mp_file_history_dir_func == NULL) {
1441 		return (0);
1442 	}
1443 
1444 	pos = 0;
1445 	err = 0;
1446 
1447 	dirp = opendir(dir);
1448 	if (dirp == NULL)
1449 		return (0);
1450 
1451 	do {
1452 		nml = PATH_MAX;
1453 		err = dp_readdir(dirp, &pos, nm, &nml, &ino);
1454 		if (err != 0) {
1455 			NDMP_LOG(LOG_DEBUG,
1456 			    "%d reading pos %u dir \"%s\"", err, pos, dir);
1457 			break;
1458 		}
1459 		if (nml == 0)
1460 			break;
1461 		nm[nml] = '\0';
1462 
1463 		if (pino == ROOT_INODE) {
1464 			if (rootfs_dot_or_dotdot(nm))
1465 				ino = ROOT_INODE;
1466 		} else if (ino == nlp->nlp_bkdirino && IS_DOTDOT(nm)) {
1467 			NDMP_LOG(LOG_DEBUG, "nm(%s): %lu", nm, ino);
1468 			ino = ROOT_INODE;
1469 		}
1470 
1471 		if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)ino))
1472 			continue;
1473 
1474 		err = (*params->mp_file_history_dir_func)(cbp->fh_cookie, nm,
1475 		    ino, pino);
1476 		if (err < 0) {
1477 			NDMP_LOG(LOG_DEBUG, "\"%s/%s\": %d", dir, nm, err);
1478 			break;
1479 		}
1480 
1481 		/*
1482 		 * This is a requirement by some DMA's (net_vault) that during
1483 		 * the incremental backup, the node info should also be sent
1484 		 * along with the dir info for all directories leading to a
1485 		 * backed up file.
1486 		 */
1487 		if (ndmp_fhinode) {
1488 			struct stat64 ret_attr;
1489 
1490 			(void) strlcpy(dirpath, dir, PATH_MAX);
1491 			(void) strlcat(dirpath, "/", PATH_MAX);
1492 			(void) strlcat(dirpath, nm, PATH_MAX);
1493 			err = stat64(dirpath, &ret_attr);
1494 			if (err != 0) {
1495 				NDMP_LOG(LOG_DEBUG,
1496 				    "Error looking up %s", nm);
1497 				break;
1498 			}
1499 
1500 			if (S_ISDIR(ret_attr.st_mode)) {
1501 				err = (*params->mp_file_history_node_func)(cbp->
1502 				    fh_cookie, ino, &ret_attr, 0);
1503 				if (err < 0) {
1504 					NDMP_LOG(LOG_DEBUG, "\"%s/\": %d",
1505 					    dir, err);
1506 					break;
1507 				}
1508 			}
1509 		}
1510 	} while (err == 0);
1511 
1512 	(void) closedir(dirp);
1513 	return (err);
1514 }
1515 
1516 
1517 /*
1518  * ndmpd_file_history_node
1519  *
1520  * Generate file history node information posts
1521  */
1522 /*ARGSUSED*/
1523 int
1524 ndmpd_file_history_node(lbr_fhlog_call_backs_t *cbp, char *dir, char *file,
1525     struct stat64 *stp, u_longlong_t off)
1526 {
1527 	int err;
1528 	ulong_t ino;
1529 	ndmp_lbr_params_t *nlp;
1530 	ndmpd_module_params_t *params;
1531 
1532 	if (!cbp) {
1533 		err = -1;
1534 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
1535 	} else if (!cbp->fh_cookie) {
1536 		err = -1;
1537 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
1538 	} else if (!dir) {
1539 		err = -1;
1540 		NDMP_LOG(LOG_DEBUG, "dir is NULL");
1541 	} else if (!file) {
1542 		err = -1;
1543 		NDMP_LOG(LOG_DEBUG, "file is NULL");
1544 	} else if (!stp) {
1545 		err = -1;
1546 		NDMP_LOG(LOG_DEBUG, "stp is NULL");
1547 	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
1548 		err = -1;
1549 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
1550 	} else
1551 		err = 0;
1552 
1553 	if (err != 0)
1554 		return (0);
1555 
1556 	NDMP_LOG(LOG_DEBUG, "d(%s), f(%s)", dir, file);
1557 
1558 	err = 0;
1559 	if (fh_requested(cbp->fh_cookie) == TRUE) {
1560 		if (stp->st_ino == nlp->nlp_bkdirino) {
1561 			ino = ROOT_INODE;
1562 			NDMP_LOG(LOG_DEBUG,
1563 			    "bkroot %d -> %d", stp->st_ino, ROOT_INODE);
1564 		} else {
1565 			ino = stp->st_ino;
1566 		}
1567 
1568 		params = get_params(cbp->fh_cookie);
1569 		if (params == NULL || params->mp_file_history_node_func == NULL)
1570 			err = -1;
1571 		else if ((err = (*params->mp_file_history_node_func)(cbp->
1572 		    fh_cookie, ino, stp, 0)) < 0)
1573 			NDMP_LOG(LOG_DEBUG, "\"%s/\": %d", dir, file, err);
1574 
1575 	}
1576 
1577 	return (err);
1578 }
1579 
1580 
1581 /*
1582  * ndmpd_path_restored
1583  *
1584  * Mark the specified path as a restored path
1585  */
1586 /*ARGSUSED*/
1587 int
1588 ndmpd_path_restored(lbr_fhlog_call_backs_t *cbp, char *name, struct stat64 *stp,
1589     u_longlong_t ll_pos)
1590 {
1591 	int rv;
1592 	ndmp_name *entp;
1593 	ndmp_lbr_params_t *nlp;
1594 	ndmpd_module_params_t *params;
1595 	int pos =  (int)ll_pos;
1596 
1597 	if (cbp == NULL) {
1598 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
1599 		return (-1);
1600 	}
1601 	if (name == NULL) {
1602 		NDMP_LOG(LOG_DEBUG, "name is NULL");
1603 		return (-1);
1604 	}
1605 
1606 	NDMP_LOG(LOG_DEBUG, "name: \"%s\", pos: %d",
1607 	    name, pos);
1608 
1609 	if ((nlp = ndmp_get_nlp(cbp->fh_cookie)) == NULL) {
1610 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
1611 		return (-1);
1612 	}
1613 	if (pos < 0 || pos >= nlp->nlp_nfiles) {
1614 		NDMP_LOG(LOG_DEBUG, "Invalid pos: %d", pos);
1615 		return (-1);
1616 	}
1617 	params = get_params(cbp->fh_cookie);
1618 	if (params == NULL || params->mp_file_recovered_func == NULL)
1619 		return (-1);
1620 
1621 	rv = 0;
1622 	if (!nlp->nlp_restored[pos]) {
1623 		entp = (ndmp_name *)MOD_GETNAME(params, pos);
1624 		if (entp && entp->name)
1625 			name = entp->name;
1626 
1627 		if ((rv = MOD_FILERECOVERD(params, name, 0)) >= 0)
1628 			nlp->nlp_restored[pos] = TRUE;
1629 	}
1630 
1631 	return (rv);
1632 }
1633 
1634 
1635 /*
1636  * dp_readdir
1637  *
1638  * Reads the entry of the directory and provides other information
1639  * such as i-number, name, length and saves the dir entry position
1640  * in a cookie for future calls.
1641  */
1642 int
1643 dp_readdir(DIR *dirp, unsigned long *cookiep, char *name, int *n_namep,
1644     unsigned long *fileidp)
1645 {
1646 	struct dirent *entp;
1647 	int err = errno;
1648 
1649 	if ((entp = readdir(dirp)) == 0) {
1650 		if (err == errno) {
1651 			*n_namep = 0;
1652 			return (0);
1653 		}
1654 		return (errno);
1655 	}
1656 
1657 	*fileidp = entp->d_ino;
1658 	(void) strlcpy(name, entp->d_name, *n_namep);
1659 	*n_namep = entp->d_reclen + 1;
1660 	*cookiep = telldir(dirp);
1661 	return (0);
1662 }
1663