xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_fhistory.c (revision c65ebfc7045424bd04a6c7719a27b0ad3399ad54)
1 /*
2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * BSD 3 Clause License
7  *
8  * Copyright (c) 2007, The Storage Networking Industry Association.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 	- Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  *
16  * 	- Redistributions in binary form must reproduce the above copyright
17  *	  notice, this list of conditions and the following disclaimer in
18  *	  the documentation and/or other materials provided with the
19  *	  distribution.
20  *
21  *	- Neither the name of The Storage Networking Industry Association (SNIA)
22  *	  nor the names of its contributors may be used to endorse or promote
23  *	  products derived from this software without specific prior written
24  *	  permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 
41 /*
42  * File history callback functions called by backup modules. NDMP file history
43  * supports 2 file history models: path based and inode/directory based.
44  * Backup/recover modules similar to unix dump/restore utilize the
45  * inode/directory based model. During the filesystem scan pass,
46  * ndmpd_file_history_dir() is called. During the file backup pass,
47  * ndmpd_file_history_node() is called. This model is appropriate for
48  * modules whose code is structured such that file name and file attribute
49  * data is not available at the same time. Backup/recover modules similar
50  * to tar or cpio utilize the path based model. The simple dump/restore module
51  * included with the SDK uses the path based model.
52  */
53 
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <dirent.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include "ndmpd.h"
61 #include <dirent.h>
62 #include <bitmap.h>
63 
64 
65 #define	N_PATH_ENTRIES	1000
66 #define	N_FILE_ENTRIES	N_PATH_ENTRIES
67 #define	N_DIR_ENTRIES	1000
68 #define	N_NODE_ENTRIES	1000
69 
70 /* Figure an average of 32 bytes per path name */
71 #define	PATH_NAMEBUF_SIZE	(N_PATH_ENTRIES * 32)
72 
73 /* Figure an average of 16 bytes per file name */
74 #define	DIR_NAMEBUF_SIZE	(N_PATH_ENTRIES * 16)
75 
76 static boolean_t fh_requested(void *cookie);
77 static void ndmpd_file_history_cleanup_v2(ndmpd_session_t *session,
78     boolean_t send_flag);
79 static void ndmpd_file_history_cleanup_v3(ndmpd_session_t *session,
80     boolean_t send_flag);
81 static ndmpd_module_params_t *get_params(void *cookie);
82 
83 
84 /*
85  * Each file history as a separate message to the client.
86  */
87 static int ndmp_syncfh = 0;
88 
89 
90 /*
91  * ************************************************************************
92  * NDMP V2 HANDLERS
93  * ************************************************************************
94  */
95 
96 /*
97  * ndmpd_api_file_history_path_v2
98  *
99  * Add a file history path entry to the buffer.
100  * History data is buffered until the buffer is filled.
101  * Full buffers are then sent to the client.
102  *
103  * Parameters:
104  *   cookie   (input) - session pointer.
105  *   name     (input) - file name.
106  *		      NULL forces buffered data to be sent.
107  *   file_stat (input) - file status pointer.
108  *   fh_info  (input) - data stream position of file data used during
109  *		      fast restore.
110  *
111  * Returns:
112  *   0 - success
113  *  -1 - error
114  */
115 int
116 ndmpd_api_file_history_path_v2(void *cookie, char *name,
117     struct stat64 *file_stat, u_longlong_t fh_info)
118 {
119 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
120 	ndmp_fh_unix_path *entry;
121 
122 	if (name == NULL && session->ns_fh.fh_path_index == 0)
123 		return (0);
124 
125 	/*
126 	 * If the buffer does not have space
127 	 * for the current entry, send the buffered data to the client.
128 	 * A NULL name indicates that any buffered data should be sent.
129 	 */
130 	if (name == NULL ||
131 	    (ndmp_syncfh && session->ns_fh.fh_path_index != 0) ||
132 	    session->ns_fh.fh_path_index == N_PATH_ENTRIES ||
133 	    session->ns_fh.fh_path_name_buf_index + strlen(name) + 1 >
134 	    PATH_NAMEBUF_SIZE) {
135 		ndmp_fh_add_unix_path_request request;
136 
137 		NDMP_LOG(LOG_DEBUG,
138 		    "sending %ld entries", session->ns_fh.fh_path_index);
139 
140 		request.paths.paths_val = session->ns_fh.fh_path_entries;
141 		request.paths.paths_len = session->ns_fh.fh_path_index;
142 
143 		if (ndmp_send_request_lock(session->ns_connection,
144 		    NDMP_FH_ADD_UNIX_PATH, NDMP_NO_ERR, (void *) &request,
145 		    0) < 0) {
146 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
147 			return (-1);
148 		}
149 		session->ns_fh.fh_path_index = 0;
150 		session->ns_fh.fh_path_name_buf_index = 0;
151 	}
152 	if (name == NULL)
153 		return (0);
154 
155 	if (session->ns_fh.fh_path_entries == 0) {
156 		session->ns_fh.fh_path_entries = ndmp_malloc(N_PATH_ENTRIES *
157 		    sizeof (ndmp_fh_unix_path));
158 		if (session->ns_fh.fh_path_entries == 0)
159 			return (-1);
160 	}
161 	if (session->ns_fh.fh_path_name_buf == 0) {
162 		session->ns_fh.fh_path_name_buf =
163 		    ndmp_malloc(PATH_NAMEBUF_SIZE);
164 		if (session->ns_fh.fh_path_name_buf == 0)
165 			return (-1);
166 	}
167 	entry = &session->ns_fh.fh_path_entries[session->ns_fh.fh_path_index];
168 	ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype);
169 
170 	entry->name = &session->
171 	    ns_fh.fh_path_name_buf[session->ns_fh.fh_path_name_buf_index];
172 	(void) strlcpy(entry->name, name, PATH_NAMEBUF_SIZE);
173 	session->ns_fh.fh_path_name_buf_index += strlen(name) + 1;
174 	entry->fstat.mtime = (ulong_t)file_stat->st_mtime;
175 	entry->fstat.atime = (ulong_t)file_stat->st_atime;
176 	entry->fstat.ctime = (ulong_t)file_stat->st_ctime;
177 	entry->fstat.uid = file_stat->st_uid;
178 	entry->fstat.gid = file_stat->st_gid;
179 	entry->fstat.mode = (file_stat->st_mode) & 0x0fff;
180 	entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size);
181 	entry->fstat.fh_info = long_long_to_quad((u_longlong_t)fh_info);
182 	session->ns_fh.fh_path_index++;
183 	return (0);
184 }
185 
186 
187 /*
188  * ndmpd_api_file_history_dir_v2
189  *
190  * Add a file history dir entry to the buffer.
191  * History data is buffered until the buffer is filled.
192  * Full buffers are then sent to the client.
193  *
194  * Parameters:
195  *   cookie (input) - session pointer.
196  *   name   (input) - file name.
197  *		    NULL forces buffered data to be sent.
198  *   node   (input) - file inode.
199  *   parent (input) - file parent inode.
200  *		    Should equal node if the file is the root of
201  *		    the filesystem and has no parent.
202  *
203  * Returns:
204  *   0 - success
205  *  -1 - error
206  */
207 int
208 ndmpd_api_file_history_dir_v2(void *cookie, char *name, ulong_t node,
209     ulong_t parent)
210 {
211 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
212 	ndmp_fh_unix_dir *entry;
213 
214 	if (name == NULL && session->ns_fh.fh_dir_index == 0)
215 		return (0);
216 
217 	/*
218 	 * If the buffer does not have space for the current entry,
219 	 * send the buffered data to the client. A NULL name indicates
220 	 * that any buffered data should be sent.
221 	 */
222 	if (name == NULL ||
223 	    (ndmp_syncfh && session->ns_fh.fh_dir_index != 0) ||
224 	    session->ns_fh.fh_dir_index == N_DIR_ENTRIES ||
225 	    session->ns_fh.fh_dir_name_buf_index + strlen(name) + 1 >
226 	    DIR_NAMEBUF_SIZE) {
227 		ndmp_fh_add_unix_dir_request request;
228 
229 		NDMP_LOG(LOG_DEBUG,
230 		    "sending %ld entries", session->ns_fh.fh_dir_index);
231 
232 		request.dirs.dirs_val = session->ns_fh.fh_dir_entries;
233 		request.dirs.dirs_len = session->ns_fh.fh_dir_index;
234 		if (ndmp_send_request_lock(session->ns_connection,
235 		    NDMP_FH_ADD_UNIX_DIR, NDMP_NO_ERR, (void *) &request,
236 		    0) < 0) {
237 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
238 			return (-1);
239 		}
240 		session->ns_fh.fh_dir_index = 0;
241 		session->ns_fh.fh_dir_name_buf_index = 0;
242 	}
243 	if (name == NULL)
244 		return (0);
245 
246 	if (session->ns_fh.fh_dir_entries == 0) {
247 		session->ns_fh.fh_dir_entries = ndmp_malloc(N_DIR_ENTRIES
248 		    * sizeof (ndmp_fh_unix_dir));
249 		if (session->ns_fh.fh_dir_entries == 0)
250 			return (-1);
251 	}
252 	if (session->ns_fh.fh_dir_name_buf == 0) {
253 		session->ns_fh.fh_dir_name_buf = ndmp_malloc(DIR_NAMEBUF_SIZE);
254 		if (session->ns_fh.fh_dir_name_buf == 0)
255 			return (-1);
256 	}
257 	entry = &session->ns_fh.fh_dir_entries[session->ns_fh.fh_dir_index];
258 
259 	entry->name = &session->
260 	    ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index];
261 	(void) strlcpy(&session->
262 	    ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index],
263 	    name, PATH_NAMEBUF_SIZE);
264 	session->ns_fh.fh_dir_name_buf_index += strlen(name) + 1;
265 
266 	entry->node = node;
267 	entry->parent = parent;
268 
269 	session->ns_fh.fh_dir_index++;
270 	return (0);
271 }
272 
273 
274 /*
275  * ndmpd_api_file_history_node_v2
276  *
277  * Add a file history node entry to the buffer.
278  * History data is buffered until the buffer is filled.
279  * Full buffers are then sent to the client.
280  *
281  * Parameters:
282  *   cookie   (input) - session pointer.
283  *   node     (input) - file inode.
284  *	      must match a node from a prior ndmpd_api_file_history_dir()
285  *		      call.
286  *   file_stat (input) - file status pointer.
287  *		      0 forces buffered data to be sent.
288  *   fh_info  (input) - data stream position of file data used during
289  *		      fast restore.
290  *
291  * Returns:
292  *   0 - success
293  *  -1 - error.
294  */
295 int
296 ndmpd_api_file_history_node_v2(void *cookie, ulong_t node,
297     struct stat64 *file_stat, u_longlong_t fh_info)
298 {
299 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
300 	ndmp_fh_unix_node *entry;
301 
302 	if (file_stat == NULL && session->ns_fh.fh_node_index == 0)
303 		return (-1);
304 
305 	/*
306 	 * If the buffer does not have space
307 	 * for the current entry, send the buffered data to the client.
308 	 * A 0 file_stat pointer indicates that any buffered data should
309 	 * be sent.
310 	 */
311 	if (file_stat == NULL ||
312 	    (ndmp_syncfh && session->ns_fh.fh_node_index != 0) ||
313 	    session->ns_fh.fh_node_index == N_NODE_ENTRIES) {
314 		ndmp_fh_add_unix_node_request request;
315 
316 		NDMP_LOG(LOG_DEBUG,
317 		    "sending %ld entries", session->ns_fh.fh_node_index);
318 
319 		request.nodes.nodes_val = session->ns_fh.fh_node_entries;
320 		request.nodes.nodes_len = session->ns_fh.fh_node_index;
321 		/*
322 		 * Need to send Dir entry as well. Since Dir entry is more than
323 		 * Node entry, we may send a Node entry that hasn't have
324 		 * its dir entry sent. Therefore, we need to flush Dir entry
325 		 * as well everytime the Dir entry is send.
326 		 */
327 		(void) ndmpd_api_file_history_dir_v2(session, 0, 0, 0);
328 
329 		if (ndmp_send_request_lock(session->ns_connection,
330 		    NDMP_FH_ADD_UNIX_NODE, NDMP_NO_ERR, (void *) &request,
331 		    0) < 0) {
332 			NDMP_LOG(LOG_DEBUG, "Sending file history data");
333 			return (-1);
334 		}
335 		session->ns_fh.fh_node_index = 0;
336 	}
337 	if (file_stat == NULL)
338 		return (0);
339 
340 	if (session->ns_fh.fh_node_entries == 0) {
341 		session->ns_fh.fh_node_entries = ndmp_malloc(N_NODE_ENTRIES
342 		    * sizeof (ndmp_fh_unix_node));
343 		if (session->ns_fh.fh_node_entries == 0)
344 			return (-1);
345 	}
346 	entry = &session->ns_fh.fh_node_entries[session->ns_fh.fh_node_index];
347 	ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype);
348 
349 	entry->node = node;
350 	entry->fstat.mtime = (ulong_t)file_stat->st_mtime;
351 	entry->fstat.atime = (ulong_t)file_stat->st_atime;
352 	entry->fstat.ctime = (ulong_t)file_stat->st_ctime;
353 	entry->fstat.uid = file_stat->st_uid;
354 	entry->fstat.gid = file_stat->st_gid;
355 	entry->fstat.mode = (file_stat->st_mode) & 0x0fff;
356 	entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size);
357 	entry->fstat.fh_info = long_long_to_quad(fh_info);
358 
359 	session->ns_fh.fh_node_index++;
360 	return (0);
361 }
362 
363 
364 /*
365  * ************************************************************************
366  * NDMP V3 HANDLERS
367  * ************************************************************************
368  */
369 
370 /*
371  * ndmpd_api_file_history_file_v3
372  *
373  * Add a file history file entry to the buffer.
374  * History data is buffered until the buffer is filled.
375  * Full buffers are then sent to the client.
376  *
377  * Parameters:
378  *   cookie   (input) - session pointer.
379  *   name     (input) - file name.
380  *		      NULL forces buffered data to be sent.
381  *   file_stat (input) - file status pointer.
382  *   fh_info  (input) - data stream position of file data used during
383  *		      fast restore.
384  *
385  * Returns:
386  *   0 - success
387  *  -1 - error
388  */
389 int
390 ndmpd_api_file_history_file_v3(void *cookie, char *name,
391     struct stat64 *file_stat, u_longlong_t fh_info)
392 {
393 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
394 	ndmp_file_v3 *file_entry;
395 	ndmp_file_name_v3 *file_name_entry;
396 	ndmp_file_stat_v3 *file_stat_entry;
397 	ndmp_fh_add_file_request_v3 request;
398 
399 	if (name == NULL && session->ns_fh_v3.fh_file_index == 0)
400 		return (0);
401 
402 	/*
403 	 * If the buffer does not have space
404 	 * for the current entry, send the buffered data to the client.
405 	 * A NULL name indicates that any buffered data should be sent.
406 	 */
407 	if (name == NULL ||
408 	    session->ns_fh_v3.fh_file_index == N_FILE_ENTRIES ||
409 	    session->ns_fh_v3.fh_file_name_buf_index + strlen(name) + 1 >
410 	    PATH_NAMEBUF_SIZE) {
411 
412 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
413 		    session->ns_fh_v3.fh_file_index);
414 
415 		request.files.files_len = session->ns_fh_v3.fh_file_index;
416 		request.files.files_val = session->ns_fh_v3.fh_files;
417 
418 		if (ndmp_send_request_lock(session->ns_connection,
419 		    NDMP_FH_ADD_FILE, NDMP_NO_ERR, (void *) &request, 0) < 0) {
420 			NDMP_LOG(LOG_DEBUG,
421 			    "Sending ndmp_fh_add_file request");
422 			return (-1);
423 		}
424 
425 		session->ns_fh_v3.fh_file_index = 0;
426 		session->ns_fh_v3.fh_file_name_buf_index = 0;
427 	}
428 
429 	if (name == NULL)
430 		return (0);
431 
432 	if (session->ns_fh_v3.fh_files == 0) {
433 		session->ns_fh_v3.fh_files = ndmp_malloc(sizeof (ndmp_file_v3) *
434 		    N_FILE_ENTRIES);
435 		if (session->ns_fh_v3.fh_files == 0)
436 			return (-1);
437 	}
438 
439 	if (session->ns_fh_v3.fh_file_names == 0) {
440 		session->ns_fh_v3.fh_file_names =
441 		    ndmp_malloc(sizeof (ndmp_file_name_v3) * N_FILE_ENTRIES);
442 		if (session->ns_fh_v3.fh_file_names == 0)
443 			return (-1);
444 	}
445 
446 	if (session->ns_fh_v3.fh_file_name_buf == 0) {
447 		session->ns_fh_v3.fh_file_name_buf =
448 		    ndmp_malloc(sizeof (char) * PATH_NAMEBUF_SIZE);
449 		if (session->ns_fh_v3.fh_file_name_buf == 0)
450 			return (-1);
451 	}
452 
453 	if (session->ns_fh_v3.fh_file_stats == 0) {
454 		session->ns_fh_v3.fh_file_stats =
455 		    ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_FILE_ENTRIES);
456 		if (session->ns_fh_v3.fh_file_stats == 0)
457 			return (-1);
458 	}
459 
460 	file_entry =
461 	    &session->ns_fh_v3.fh_files[session->ns_fh_v3.fh_file_index];
462 	file_name_entry =
463 	    &session->ns_fh_v3.fh_file_names[session->ns_fh_v3.fh_file_index];
464 	file_stat_entry =
465 	    &session->ns_fh_v3.fh_file_stats[session->ns_fh_v3.fh_file_index];
466 	file_entry->names.names_len = 1;
467 	file_entry->names.names_val = file_name_entry;
468 	file_entry->stats.stats_len = 1;
469 	file_entry->stats.stats_val = file_stat_entry;
470 	file_entry->node = long_long_to_quad(file_stat->st_ino);
471 	file_entry->fh_info = long_long_to_quad(fh_info);
472 
473 	file_name_entry->fs_type = NDMP_FS_UNIX;
474 	file_name_entry->ndmp_file_name_v3_u.unix_name =
475 	    &session->ns_fh_v3.fh_file_name_buf[session->
476 	    ns_fh_v3.fh_file_name_buf_index];
477 	(void) strlcpy(&session->ns_fh_v3.fh_file_name_buf[session->
478 	    ns_fh_v3.fh_file_name_buf_index], name, PATH_NAMEBUF_SIZE);
479 	session->ns_fh_v3.fh_file_name_buf_index += strlen(name) + 1;
480 	ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype);
481 
482 	file_stat_entry->invalid = 0;
483 	file_stat_entry->fs_type = NDMP_FS_UNIX;
484 	file_stat_entry->mtime = file_stat->st_mtime;
485 	file_stat_entry->atime = file_stat->st_atime;
486 	file_stat_entry->ctime = file_stat->st_ctime;
487 	file_stat_entry->owner = file_stat->st_uid;
488 	file_stat_entry->group = file_stat->st_gid;
489 	file_stat_entry->fattr = file_stat->st_mode & 0x0fff;
490 	file_stat_entry->size =
491 	    long_long_to_quad((u_longlong_t)file_stat->st_size);
492 	file_stat_entry->links = file_stat->st_nlink;
493 
494 	session->ns_fh_v3.fh_file_index++;
495 
496 	return (0);
497 }
498 
499 
500 /*
501  * ndmpd_api_file_history_dir_v3
502  *
503  * Add a file history dir entry to the buffer.
504  * History data is buffered until the buffer is filled.
505  * Full buffers are then sent to the client.
506  *
507  * Parameters:
508  *   cookie (input) - session pointer.
509  *   name   (input) - file name.
510  *		    NULL forces buffered data to be sent.
511  *   node   (input) - file inode.
512  *   parent (input) - file parent inode.
513  *		    Should equal node if the file is the root of
514  *		    the filesystem and has no parent.
515  *
516  * Returns:
517  *   0 - success
518  *  -1 - error
519  */
520 int
521 ndmpd_api_file_history_dir_v3(void *cookie, char *name, ulong_t node,
522     ulong_t parent)
523 {
524 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
525 	ndmp_dir_v3 *dir_entry;
526 	ndmp_file_name_v3 *dir_name_entry;
527 	ndmp_fh_add_dir_request_v3 request;
528 
529 	if (name == NULL && session->ns_fh_v3.fh_dir_index == 0)
530 		return (0);
531 
532 	/*
533 	 * If the buffer does not have space
534 	 * for the current entry, send the buffered data to the client.
535 	 * A NULL name indicates that any buffered data should be sent.
536 	 */
537 	if (name == NULL ||
538 	    session->ns_fh_v3.fh_dir_index == N_DIR_ENTRIES ||
539 	    session->ns_fh_v3.fh_dir_name_buf_index + strlen(name) + 1 >
540 	    DIR_NAMEBUF_SIZE) {
541 
542 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
543 		    session->ns_fh_v3.fh_dir_index);
544 
545 		request.dirs.dirs_val = session->ns_fh_v3.fh_dirs;
546 		request.dirs.dirs_len = session->ns_fh_v3.fh_dir_index;
547 
548 		if (ndmp_send_request_lock(session->ns_connection,
549 		    NDMP_FH_ADD_DIR, NDMP_NO_ERR, (void *) &request, 0) < 0) {
550 			NDMP_LOG(LOG_DEBUG,
551 			    "Sending ndmp_fh_add_dir request");
552 			return (-1);
553 		}
554 
555 		session->ns_fh_v3.fh_dir_index = 0;
556 		session->ns_fh_v3.fh_dir_name_buf_index = 0;
557 	}
558 
559 	if (name == NULL)
560 		return (0);
561 
562 	if (session->ns_fh_v3.fh_dirs == 0) {
563 		session->ns_fh_v3.fh_dirs =
564 		    ndmp_malloc(sizeof (ndmp_dir_v3) * N_DIR_ENTRIES);
565 		if (session->ns_fh_v3.fh_dirs == 0)
566 			return (-1);
567 	}
568 
569 	if (session->ns_fh_v3.fh_dir_names == 0) {
570 		session->ns_fh_v3.fh_dir_names =
571 		    ndmp_malloc(sizeof (ndmp_file_name_v3) * N_DIR_ENTRIES);
572 		if (session->ns_fh_v3.fh_dir_names == 0)
573 			return (-1);
574 	}
575 
576 	if (session->ns_fh_v3.fh_dir_name_buf == 0) {
577 		session->ns_fh_v3.fh_dir_name_buf =
578 		    ndmp_malloc(sizeof (char) * DIR_NAMEBUF_SIZE);
579 		if (session->ns_fh_v3.fh_dir_name_buf == 0)
580 			return (-1);
581 	}
582 
583 	dir_entry = &session->ns_fh_v3.fh_dirs[session->ns_fh_v3.fh_dir_index];
584 	dir_name_entry =
585 	    &session->ns_fh_v3.fh_dir_names[session->ns_fh_v3.fh_dir_index];
586 
587 	dir_name_entry->fs_type = NDMP_FS_UNIX;
588 	dir_name_entry->ndmp_file_name_v3_u.unix_name =
589 	    &session->ns_fh_v3.fh_dir_name_buf[session->
590 	    ns_fh_v3.fh_dir_name_buf_index];
591 
592 	(void) strlcpy(&session->ns_fh_v3.fh_dir_name_buf[session->
593 	    ns_fh_v3.fh_dir_name_buf_index], name, PATH_NAMEBUF_SIZE);
594 	session->ns_fh_v3.fh_dir_name_buf_index += strlen(name) + 1;
595 
596 	dir_entry->names.names_len = 1;
597 	dir_entry->names.names_val = dir_name_entry;
598 	dir_entry->node = long_long_to_quad(node);
599 	dir_entry->parent = long_long_to_quad(parent);
600 
601 	session->ns_fh_v3.fh_dir_index++;
602 
603 	return (0);
604 }
605 
606 
607 /*
608  * ndmpd_api_file_history_node_v3
609  *
610  * Add a file history node entry to the buffer.
611  * History data is buffered until the buffer is filled.
612  * Full buffers are then sent to the client.
613  *
614  * Parameters:
615  *   cookie   (input) - session pointer.
616  *   node     (input) - file inode.
617  *		must match a node from a prior ndmpd_api_file_history_dir()
618  *		      call.
619  *   file_stat (input) - file status pointer.
620  *		      0 forces buffered data to be sent.
621  *   fh_info  (input) - data stream position of file data used during
622  *		      fast restore.
623  *
624  * Returns:
625  *   0 - success
626  *  -1 - error.
627  */
628 int
629 ndmpd_api_file_history_node_v3(void *cookie, ulong_t node,
630     struct stat64 *file_stat, u_longlong_t fh_info)
631 {
632 	ndmpd_session_t *session = (ndmpd_session_t *)cookie;
633 	ndmp_node_v3 *node_entry;
634 	ndmp_file_stat_v3 *file_stat_entry;
635 	ndmp_fh_add_node_request_v3 request;
636 
637 	if (file_stat == NULL && session->ns_fh_v3.fh_node_index == 0)
638 		return (0);
639 
640 	/*
641 	 * If the buffer does not have space
642 	 * for the current entry, send the buffered data to the client.
643 	 * A 0 file_stat pointer indicates that any buffered data should
644 	 * be sent.
645 	 */
646 	if (file_stat == NULL ||
647 	    session->ns_fh_v3.fh_node_index == N_NODE_ENTRIES) {
648 		NDMP_LOG(LOG_DEBUG, "sending %ld entries",
649 		    session->ns_fh_v3.fh_node_index);
650 
651 		/*
652 		 * Need to send Dir entry as well. Since Dir entry is more
653 		 * than a Node entry, we may send a Node entry that hasn't
654 		 * had its Dir entry sent. Therefore, we need to flush Dir
655 		 * entry as well every time the Dir entry is sent.
656 		 */
657 		(void) ndmpd_api_file_history_dir_v3(session, 0, 0, 0);
658 
659 		request.nodes.nodes_len = session->ns_fh_v3.fh_node_index;
660 		request.nodes.nodes_val = session->ns_fh_v3.fh_nodes;
661 
662 		if (ndmp_send_request_lock(session->ns_connection,
663 		    NDMP_FH_ADD_NODE,
664 		    NDMP_NO_ERR, (void *) &request, 0) < 0) {
665 			NDMP_LOG(LOG_DEBUG,
666 			    "Sending ndmp_fh_add_node request");
667 			return (-1);
668 		}
669 
670 		session->ns_fh_v3.fh_node_index = 0;
671 	}
672 
673 	if (file_stat == NULL)
674 		return (0);
675 
676 	if (session->ns_fh_v3.fh_nodes == 0) {
677 		session->ns_fh_v3.fh_nodes =
678 		    ndmp_malloc(sizeof (ndmp_node_v3) * N_NODE_ENTRIES);
679 		if (session->ns_fh_v3.fh_nodes == 0)
680 			return (-1);
681 	}
682 
683 	if (session->ns_fh_v3.fh_node_stats == 0) {
684 		session->ns_fh_v3.fh_node_stats =
685 		    ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_NODE_ENTRIES);
686 		if (session->ns_fh_v3.fh_node_stats == 0)
687 			return (-1);
688 	}
689 
690 	node_entry =
691 	    &session->ns_fh_v3.fh_nodes[session->ns_fh_v3.fh_node_index];
692 
693 	file_stat_entry =
694 	    &session->ns_fh_v3.fh_node_stats[session->ns_fh_v3.fh_node_index];
695 	ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype);
696 
697 	file_stat_entry->invalid = 0;
698 	file_stat_entry->fs_type = NDMP_FS_UNIX;
699 	file_stat_entry->mtime = file_stat->st_mtime;
700 	file_stat_entry->atime = file_stat->st_atime;
701 	file_stat_entry->ctime = file_stat->st_ctime;
702 	file_stat_entry->owner = file_stat->st_uid;
703 	file_stat_entry->group = file_stat->st_gid;
704 	file_stat_entry->fattr = file_stat->st_mode & 0x0fff;
705 	file_stat_entry->size =
706 	    long_long_to_quad((u_longlong_t)file_stat->st_size);
707 	file_stat_entry->links = file_stat->st_nlink;
708 
709 	node_entry->stats.stats_len = 1;
710 	node_entry->stats.stats_val = file_stat_entry;
711 	node_entry->node = long_long_to_quad((u_longlong_t)node);
712 	node_entry->fh_info = long_long_to_quad(fh_info);
713 
714 	session->ns_fh_v3.fh_node_index++;
715 
716 	return (0);
717 }
718 
719 
720 /*
721  * ************************************************************************
722  * NDMP V4 HANDLERS
723  * ************************************************************************
724  */
725 
726 
727 /*
728  * ndmpd_fhpath_v3_cb
729  *
730  * Callback function for file history path information
731  */
732 int
733 ndmpd_fhpath_v3_cb(lbr_fhlog_call_backs_t *cbp, char *path, struct stat64 *stp,
734     u_longlong_t off)
735 {
736 	int err;
737 	ndmp_lbr_params_t *nlp;
738 	ndmpd_module_params_t *params;
739 
740 	if (!cbp) {
741 		err = -1;
742 		NDMP_LOG(LOG_DEBUG, "cbp is NULL");
743 	} else if (!cbp->fh_cookie) {
744 		err = -1;
745 		NDMP_LOG(LOG_DEBUG, "cookie is NULL");
746 	} else if (!path) {
747 		err = -1;
748 		NDMP_LOG(LOG_DEBUG, "path is NULL");
749 	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
750 		err = -1;
751 		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
752 	} else
753 		err = 0;
754 
755 	if (err != 0)
756 		return (0);
757 
758 	NDMP_LOG(LOG_DEBUG, "pname(%s)", path);
759 
760 	err = 0;
761 	if (NLP_ISSET(nlp, NLPF_FH)) {
762 		if (!NLP_ISSET(nlp, NLPF_DIRECT)) {
763 			NDMP_LOG(LOG_DEBUG, "DAR NOT SET!");
764 			off = 0LL;
765 		}
766 
767 		params = get_params(cbp->fh_cookie);
768 		if (!params || !params->mp_file_history_path_func) {
769 			err = -1;
770 		} else {
771 			char *p =
772 			    ndmp_get_relative_path(get_backup_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