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
76static boolean_t fh_requested(void *cookie);
77static void ndmpd_file_history_cleanup_v2(ndmpd_session_t *session,
78    boolean_t send_flag);
79static void ndmpd_file_history_cleanup_v3(ndmpd_session_t *session,
80    boolean_t send_flag);
81static ndmpd_module_params_t *get_params(void *cookie);
82
83
84/*
85 * Each file history as a separate message to the client.
86 */
87static 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 */
115int
116ndmpd_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 */
207int
208ndmpd_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 */
295int
296ndmpd_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 */
389int
390ndmpd_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 */
520int
521ndmpd_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 */
628int
629ndmpd_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 */
732int
733ndmpd_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 */
789int
790ndmpd_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 */
938int
939ndmpd_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 */
1002int
1003ndmp_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*/
1039int
1040ndmpd_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 */
1108void
1109ndmpd_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 */
1155static void
1156ndmpd_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 */
1205static void
1206ndmpd_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 */
1264void
1265ndmpd_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 */
1287static ndmpd_module_params_t *
1288get_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 */
1304static boolean_t
1305fh_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*/
1331int
1332ndmpd_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 */
1377int
1378ndmpd_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*/
1523int
1524ndmpd_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*/
1587int
1588ndmpd_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 */
1642int
1643dp_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