/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2015 by Delphix. All rights reserved.
* Copyright 2016 Joyent, Inc.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
*/
/*
* zfs diff support
*/
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
#include <string.h>
#include <fcntl.h>
#include <attr.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stropts.h>
#include <pthread.h>
#include <sys/zfs_ioctl.h>
#include <libzfs.h>
#include "libzfs_impl.h"
typedef struct differ_info {
char *fromsnap;
char *frommnt;
char *tosnap;
char *tomnt;
char *ds;
char *dsmnt;
char *tmpsnap;
int zerr;
int cleanupfd;
int outputfd;
int datafd;
/*
* Given a {dsname, object id}, get the object path
*/
static int
{
int error;
errno = 0;
/* we can get stats even if we failed to get a path */
if (error == 0) {
return (0);
}
"The sys_config privilege or diff delegated permission "
"is needed\nto discover path names"));
return (-1);
} else {
"Unable to determine path or stats for "
return (-1);
}
}
/*
* stream_bytes
*
* Prints a file name out a character at a time. If the character is
* not in the range of what we consider "printable" ASCII, display it
* as an escaped 3-digit octal value. ASCII values less than a space
* are all control characters and we declare the upper end as the
* DELete character. This also is the last 7-bit ASCII character.
* We choose to treat all 8-bit ASCII as not printable for this
* application.
*/
static void
{
char c;
while ((c = *string++) != '\0') {
if (c > ' ' && c != '\\' && c < '\177') {
} else {
}
}
}
static void
{
char symbol;
case S_IFBLK:
symbol = 'B';
break;
case S_IFCHR:
symbol = 'C';
break;
case S_IFDIR:
symbol = '/';
break;
case S_IFDOOR:
symbol = '>';
break;
case S_IFIFO:
symbol = '|';
break;
case S_IFLNK:
symbol = '@';
break;
case S_IFPORT:
symbol = 'P';
break;
case S_IFSOCK:
symbol = '=';
break;
case S_IFREG:
symbol = 'F';
break;
default:
symbol = '?';
break;
}
}
static void
{
}
static void
{
if (di->timestamped)
}
else
}
static void
{
if (di->timestamped)
}
}
static void
{
if (di->timestamped)
}
}
static int
{
int change;
return (0);
/*
* Check the from and to snapshots for info on the object. If
* we get ENOENT, then the object just didn't exist in that
* snapshot. If we get ENOTSUP, then we tried to get
* info on a non-ZPL object, which we don't care about anyway.
*/
MAXPATHLEN, &fsb);
return (-1);
MAXPATHLEN, &tsb);
return (-1);
/*
* Unallocated object sharing the same meta dnode block
*/
return (0);
}
change = 0;
else
if (fobjerr) {
if (change) {
return (0);
}
return (0);
} else if (tobjerr) {
if (change) {
return (0);
}
return (0);
}
/* Simple modification or no change */
/* No apparent changes. Could we assert !this? */
return (0);
if (change) {
} else {
}
return (0);
} else {
/* file re-created or object re-used */
return (0);
}
}
static int
{
uint64_t o;
int err;
return (err);
}
return (0);
}
static int
int maxlen)
{
/* Let it slide, if in the delete queue on from side */
return (0);
}
return (-1);
}
return (0);
}
static int
{
int err;
if (err == 0) {
continue;
}
break;
}
if (err)
break;
break;
} else {
"next allocated object (> %lld) find failure"),
break;
}
}
return (-1);
return (0);
}
static void *
{
int err = 0;
return ((void *)-1);
}
for (;;) {
int rv;
do {
break;
} else if (rv == 0) {
/* end of file at a natural breaking point */
break;
}
case DDR_FREE:
break;
case DDR_INUSE:
break;
default:
break;
}
break;
}
if (err)
return ((void *)-1);
"Internal error: bad data from diff IOCTL"));
return ((void *)-1);
}
return ((void *)0);
}
static int
{
}
return (0);
}
static int
{
ZDIFF_PREFIX, getpid());
"permission is needed in order\nto create a "
"just-in-time snapshot for diffing\n"));
} else {
}
}
return (0);
}
static void
{
}
static int
const char *tosnap)
{
/*
* Can accept
* dataset@snap1
* dataset@snap1 dataset@snap2
* dataset@snap1 @snap2
* dataset@snap1 dataset
* @snap1 dataset@snap2
*/
/* only a from snapshot given, must be valid */
"Badly formed snapshot name %s"), fromsnap);
B_FALSE)) {
}
/* the to snap will be a just-in-time snap of the head */
return (make_temp_snapshot(di));
}
"Unable to determine which snapshots to compare"));
/*
* not the same dataset name, might be okay if
* tosnap is a clone of a fromsnap descendant.
*/
break;
}
break;
}
"Not an earlier snapshot from the same fs"));
} else {
}
if (tsnlen) {
} else {
return (make_temp_snapshot(di));
}
} else {
if (tsnlen) {
} else {
return (make_temp_snapshot(di));
}
}
return (0);
}
static int
{
"Cannot diff an unmounted snapshot"));
}
/* Avoid a double slash at the beginning of root-mounted datasets */
**mntpt = '\0';
return (0);
}
static int
{
char *strptr;
char *frommntpt;
/*
* first get the mountpoint for the parent dataset
*/
return (-1);
ZDIFF_SNAPDIR, ++strptr);
char *mntpt;
int err;
*strptr = '\0';
*strptr = '@';
if (err != 0)
return (-1);
}
ZDIFF_SNAPDIR, ++strptr);
return (0);
}
static int
{
return (-1);
if (get_mountpoints(di) != 0)
return (-1);
if (find_shares_object(di) != 0)
return (-1);
return (0);
}
int
{
int iocerr;
return (-1);
}
}
}
/* do the ioctl() */
if (iocerr != 0) {
"\n The sys_mount privilege or diff delegated "
"permission is needed\n to execute the "
"diff ioctl"));
"\n Not an earlier snapshot from the same fs"));
}
(void) pthread_cancel(tid);
} else {
}
}
}
return (0);
}